Upgrade to Pro — share decks privately, control downloads, hide ads and more …

[Crypto in CTF] Blockchain Security

oalieno
October 31, 2020

[Crypto in CTF] Blockchain Security

oalieno

October 31, 2020
Tweet

More Decks by oalieno

Other Decks in Technology

Transcript

  1. Blockchain A → B $3 C → A $2 ⋮

    • 去中⼼化的記帳本 • 去中⼼化:⼤家都有⼀份⼀模⼀樣的記帳本備份 • 記帳本:記錄誰給誰多少錢 A → B $3 C → A $2 ⋮ Alice Bob
  2. Blockchain • Alice 要給 Bob 1 塊錢 • 記錄⼀筆交易,並廣播給其他所有⼈ A

    → B $3 C → A $2 ⋮ A → B $3 C → A $2 ⋮ Alice Bob A → B $1
  3. Blockchain • Bob 可以偽造⼀筆交易,廣播給所有⼈ A → B $3 C →

    A $2 ⋮ A → B $3 C → A $2 ⋮ Alice Bob A → B $100
  4. Blockchain • 因此每⼀筆帳必須簽章 • 將錢從 A 轉出去的帳,要有 A 簽章才有效 A

    → B $3 C → A $2 ⋮ A → B $3 C → A $2 ⋮ Alice Bob A 簽章 C 簽章 C 簽章 A 簽章
  5. Blockchain • 但是就算有簽章,Bob 可以輕易的複製同⼀份交易再廣播出去 A → B $3 C →

    A $2 ⋮ A → B $3 C → A $2 ⋮ Alice Bob A 簽章 C 簽章 C 簽章 A 簽章 A → B $3 A 簽章
  6. Blockchain • 所以簽章的內容必須包含⼀個 id 或 timestamp 之類的 TX 0 :

    A → B $3 TX 5 : C → A $2 ⋮ TX 0 : A → B $3 TX 5 : C → A $2 ⋮ Alice Bob A 簽章 C 簽章 C 簽章 A 簽章
  7. 如何達成共識 • ⼤家都在廣播交易 • 怎麼決定誰先誰後,並保證⼤家的記帳本都長得⼀樣? TX 0 : A →

    B $3 TX 5 : C → A $2 ⋮ TX 0 : A → B $3 TX 5 : C → A $2 ⋮ Alice Bob A 簽章 C 簽章 C 簽章 A 簽章 TX 6 : A → B $1 TX 6 : B → C $1 A 簽章 B 簽章
  8. 誰有記帳權 • ⼤家決定出⼀個⼈來記帳 • 每次會記好幾個交易的帳,稱為⼀個區塊 ( Block ),不會只記⼀個交易 • 先算出哪個

    Nonce 會讓整個 Block 的 Hash 開頭是 0x00 的⼈記帳 TX 0 : A → B $3 TX 5 : C → A $2 ⋮ A 簽章 C 簽章 Nonce 0x1234 Hash 0x00abcd Alice
  9. 誰有記帳權 • 要 Hash 的東⻄包括前⼀個 Block 的 Hash,形成⼀連串的 Block Chain

    • 並且記帳的⼈能獲得獎勵,所以⼤家都會搶著記帳 TX 0 : A → B $3 Reward A $10 ⋮ A 簽章 Nonce 0x1234 Hash 0x00abcd Hash of Previous Block Alice
  10. Blockchain TX 0 : A → B A 簽章 TX

    5 : C → A C 簽章 Hash of Block 0 ⋮ TX 0 : B → D B 簽章 TX 7 : A → C A 簽章 Hash of Block 1 ⋮ Hash Hash Block 1 Block 2 Nonce Nonce
  11. 51% 攻擊 • 先轉帳 $1000 到⽬標交易所 • 等 10 個區塊

    confirmation 後,交易所認可該筆交易 • 駭客便可以將錢從交易所領出來換成法幣 Block 2 Block 11 Block 1 ... Block 2 Block 11 ... Block 12 轉帳到交易所 沒有轉帳到交易所
  12. 51% 攻擊 • 同時間,駭客⽤他的 51% 算⼒偷偷算更長的⼀條鍊 • 該條鏈裡⾯沒有他轉帳到交易所的那筆交易 • 等他從交易所領出錢錢後,就把更長的鏈廣播給⼤家

    • 這樣他的幣還在,同時⼜領了法幣出來,直接翻倍 Block 2 Block 11 Block 1 ... Block 2 Block 11 ... Block 12 轉帳到交易所 沒有轉帳到交易所
  13. POW / POS / DPOS • ⽬前主流有三種不同的⽅式決定記帳權 • Proof of

    Work ( PoW ) • 透過計算⼀個複雜的問題來獲得記帳權 • Bitcoin ( BTC ), Ethereum ( ETH ), … • Proof of Stake ( PoS ) • 持幣數量越多的越有機會獲得記帳權 • Delegated Proof of Stake ( DPoS ) • 所有節點選舉出幾個超級節點來記帳 • 還有很多變種的 PoS 機制...
  14. 錢包 • 冷錢包 ( 硬體錢包 ) : 要交易的時候再插進電腦裡 • Ledger,

    Trezor 等 • 熱錢包 ( 軟體錢包 ) : 瀏覽器插件,或是 APP 之類的 • MetaMask, Trust Wallet 等
  15. Ethereum Ropsten Network • 上課主要都是在 ropsten 測試網路中進⾏ • 不是真錢啊 •

    要錢錢可以去下⾯幾個 faucet 領 • https://faucet.metamask.io/ • https://faucet.dimensions.network/ • https://faucet.ropsten.be/
  16. Ethereum Address • Ethereum 的 Address 是把 public key 做

    keccak256 之後再取後 20 bytes • 所以 private key 跟 address 是⼀對⼀的關係 • keccak256 就是 sha3 原本的名字,但是參數跟⽬前的 sha3 有點不同
  17. Ethereum Virtual Machine ( EVM ) • 以太坊 ( Ethereum

    ) 除了存放交易們,還可以存放程式碼 • Ethereum Virtual Machine ( EVM ) • 去中⼼化的虛擬機 • 圖靈完備
  18. 智能合約 ( Smart Contract ) • ⼀個智能合約就像是⼀個物件導向中的 Class • 使⽤者可以創建智能合約,也就是存放程式碼到

    Ethereum 上⾯ • 然後跟智能合約互動,也就是呼叫合約中的函式 • 智能合約的位址跟普通錢包的位址是在同⼀個空間 pragma solidity ^0.5.0; contract test { uint data; constructor () public payable { data = 1; } function money () public { msg.sender.transfer(1 ether); } }
  19. Solidity • 存在 Ethereum 的其實是 bytecode,類似於 assembly code • Solidity

    是⾼階的語⾔,透過編譯器編譯成 bytecode • 除了 Solidity 之外,還有另⼀種⾼階語⾔叫 Vyper • Solidity 語法類似 javascript,Vyper 語法類似 python
  20. Gas https://txstreet.com/ • 在 EVM 上⾯執⾏ bytecode 是要付費的 • 每個不同的

    opcode 都會花費不同的 gas fee • 比如 ADD 會花費 3 gas • 那⼀個 gas 是多少錢?這是由交易發起⼈決定的 • 比如 gas price 設定成 10 gwei 的話,ADD 就會花費 30 gwei • Gas price 是給礦⼯的⼩費,給得越⾼你的交易能越快被驗證
  21. 單位 單位 價值多少 wei wei 1 kwei 1000 mwei 1000000

    gwei 1000000000 twei 1000000000000 pwei 1000000000000000 ether 1000000000000000000 • 在 Ethereum 中最⼩單位是 wei
  22. web3.js https://web3js.readthedocs.io/en/v1.3.0/ • Solidity 編譯成 bytecode 是放到 EVM 上執⾏的 •

    可以看成後端 • 想要跟合約互動,我們在普通的電腦上⽤ web3.js • 可以看成前端 • 除了跟合約互動,web3.js 也可以查看 Ethereum Blockchain 上的資訊 • 也有 web3.py,就只是 python 版本的 • 還有另⼀個選擇是 ethers,號稱比 web3.js 更好⽤
  23. Resources • Tutorial • cryptozombies • Online Judge • Ethernaut

    • Security Innovation Smart Contract CTF • Vulnerabilities • DASP Top 10 • SWC Registry
  24. Custom Modifier contract Test { address private owner; modifier onlyOwner()

    { require(msg.sender == owner); _; } function setOwenr(address _owner) onlyOwner public { owner = _owner; } function getOne() pure { return 1; } function getOwner() view returns(address) { return owner; } }
  25. Underflow / Overflow function withdraw(uint _amount) public { require(balances[msg.sender] -

    _amount > 0); msg.sender.transfer(_amount); balances[msg.sender] -= _amount; } • uint 往下減會 underflow • uint 其實就是 uint256,256 bits = 32 bytes • uint 的 0 往下減會變成 2256 - 1
  26. Safemath function withdraw(uint _amount) public { require(balances[msg.sender].sub(_amount) > 0); msg.sender.transfer(_amount);

    balances[msg.sender] = balances[msg.sender].sub(_amount); } • 使⽤ OpenZeppelin 寫好的 SafeMath.sol 函式庫 • 幫你安全的處理 uint 的加減乘除
  27. Random Number • 在 EVM 中基本上沒辦法產⽣安全的 random number • 能⽤的隨機來源只有

    block.timestamp, block.difficulty , block.blockhash • 但是像下⾯這個例⼦,只要部署另⼀個合約,呼叫前計算⼀下,就可以 算出⼀樣的隨機數了,因為在同⼀個 block • ⽬前最好的⽅法還是呼叫外部的中⼼化伺服器來取得隨機數 function dice() public payable { if (block.timestamp % 2 == 1) { win = true; } }
  28. 呼叫合約函式 https://www.4byte.directory/ contract A { function test (uint value) public

    {} } • 假設我們想要呼叫 A 合約的 test 函式 • 送⼀個交易給合約 A,data 欄位填該函式的 function siganture • function signature 是 keccak256('函式名稱(型態們)') 的前 4 bytes • 這個例⼦中就是 keccak256('test(uint)') 的前 4 bytes
  29. Fallback / Receive contract Test { fallback () external payable

    {} receive () external payable {} function normal_function () public {} } • fallback 和 receive 是兩個特殊的函式 • 有⼈呼叫合約,但 data 欄位是空的 • 就會執⾏ receive • 如果沒有 receive,就會執⾏ fallback • 如果 data 欄位提供的 function signature 沒有找到 match • 執⾏ fallback
  30. Reentrancy Attack function withdraw(uint _amount) public payable { require(balances[msg.sender] >=

    _amount); msg.sender.call{value: _amount}(""); balances[msg.sender] -= _amount; } • 當合約送錢給其他地址,該地址可能會是另⼀個合約 • 送錢給另⼀個合約,其實就是呼叫他的 receive / fallback • 那我們在 receive / fallback 裡⾯再去呼叫 withdraw ⼀次 • 因為 balances 還沒扣,所以第⼀⾏的 require 還是成立 • 就可以⼀直 transfer 把合約裡⾯的錢掏空
  31. require / revert / assert • require, revert 會 refund

    剩下的 gas fee • assert 會把剩下的 gas fee 都吃掉,不還你 modifier onlyOwner() { require(msg.sender == owner); _; } modifier onlyOwner() { if (msg.sender != owner) { revert(); } _; } modifier onlyOwner() { assert(msg.sender == owner); _; }
  32. delegatecall https://swcregistry.io/docs/SWC-112#proxysol • delegatecall 跟 call ⼀樣可以呼叫另⼀個合約 • 但是他會使⽤原本合約的 storage

    • 下⾯的例⼦中,callee 是惡意的話,可以接把 owner 改掉 contract Proxy { address owner; constructor() public { owner = msg.sender; } function forward(address callee, bytes _data) public { require(callee.delegatecall(_data)); } }
  33. ERC20 https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol • ⼀個代幣的標準,只要 inheritance ERC20 合約,就可以發代幣 • 代幣是存在於合約上的 •

    transfer 可以把⾃⼰的代幣轉給別⼈ • approve 可以授權別⼈花你的代幣 • transferFrom 可以花別⼈的代幣 function transfer(address _to, uint256 _value) returns (bool success) function transferFrom(address _from, address _to, uint256 _value) returns (bool success) function approve(address _spender, uint256 _value) returns (bool success)
  34. ERC721 https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC721 • 另⼀個代幣標準,也叫做 Non-Fungible Token ( NFT ) •

    每個代幣都是獨⼀無⼆的,像是蒐集郵票⼀樣 • CryptoKitties 是 NFT 的⼀個著名的例⼦