Solidity Hacks
- 作法:Attack合約會定義fallback function以返回呼叫Target合約的withdraw function實現重入
- 過程:A.attack → E.deposit → E.withdraw → A.fallback → E.withdraw → A.fallback → E.withdraw → A.fallback … 直到ethers領完為止 (balance < 1)
- 修正:將state changes提前 (i.e., balances)、或是使用ReEntrancyGuard (i.e., mutex lock);等到internal work結束之後、直到最後才呼叫external function (from Consensys) → checks-effects-interactions pattern
- 補充:msg.sender.call()導致Attack合約能利用剩餘gas執行、balances[msg.sender] = 0的位置
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 作法:利用unsigned integer的overflow or underflow特性進入漏洞
- 過程:A.attack → T.deposit → T.increaseLockTime [設定 2256 - 1 + 1 - (last deposit time + 1 week)] → lockTime = 2256 - 1 + 1 - (last deposit time + 1 week) + (last deposit time + 1 week) = 0 → T.withdraw
- 修正:在 compiler version < 0.8 時使用SafeMath;在version >= 0.8已預設自動檢查
- 補充:上述T.increaseLockTime嘗試改設 0 - (last deposit time + 1 week) 也能達成相同效果
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 作法:利用selfdestruct強制發送ethers給target (定義fallback + revert也無效) 以進入漏洞
- 過程:傳送7 ethers給A → A.attack → E.balance = 7 ethers → no winner & 無法提領當中ethers
- 修正:別使用 address(this).balance 判斷,改用state variable記錄 (e.g., balance += msg.value)
- 補充:雖然E.deposit限制單次只能收 1 ether,但是selfdestruct可以繞過此限制
- 其他:
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 作法:藉由EVM內部「Layout of State Variables in Storage」的特性 fetch對應的private data
- 過程:利用web3.js → getStorageAt(contract address, slot #) → 顯示data內容
- 修正:所有合約內的 private data 在區塊鏈上都是可見的,因此別放置機密資訊在合約中
- 補充:每個合約都會被分配 2256 slots (32 bytes each) 的儲存空間,根據state變數宣告的排列並依序儲存下來。若有幾個size較小的變數總和在32 bytes以內,則這些會被一起packed儲存在同個slot中 (影響gas optimization)。
- 測試結果:
- Contract deployed on Goerli: 0x2c40b3074f7e0dbed9142ab7a735d8e0badfad1e
- passwd: 0x7465737400000000000000000000000000000000000000000000000000000000
- Slot 0: 0x … 7b(123)
- Slot 1: 0x … 1f(31) + 01(true) + bE7d961B54a10959353a81866c5E5cF37266159E(msg.sender) → 順序不同[??]
- Slot 2: 0x7465737400000000000000000000000000000000000000000000000000000000 (passwd)
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 作法:藉由EVM內部「Mappings and Dynamic Arrays」的特性 fetch對應的private data
- 過程:利用web3.js → getStorageAt() + getArrayLocation() / getMapLocation() → 顯示data內容
- 修正:所有合約內的 private data 在區塊鏈上都是可見的,因此別放置機密資訊在合約中
- 補充:Dynamic array → currSlot儲存array length、而array element則儲存在slot# = hash(currSlot) + index * elemSize。以下圖為例,每個element為一個struct User,所以elemSize = 1 (uint256) + 1 (bytes32) = 2。
- 補充:Mapping → currSlot為空值、而array element則儲存在slot# = hash(key, slot)
- 測試結果:
- Contract deployed on Goerli: 0x2c40b3074f7e0dbed9142ab7a735d8e0badfad1e
- Slot 6: 2 (array length)
- AddUser[0]: 0, 0x7465737400000000000000000000000000000000000000000000000000000001
- AddUser[1]: 1, 0x7465737400000000000000000000000000000000000000000000000000000002
- getArrayLocation(6, 0, 2) = 111414077815863400510004064629973595961579173665589224203503662149373724986687
- getArrayLocation(6, 1, 2) = 111414077815863400510004064629973595961579173665589224203503662149373724986689
- Slot 7: (empty)
- AddUser[1]: 1, 0x7465737400000000000000000000000000000000000000000000000000000002
- getMapLocation(7, 1) = 81222191986226809103279119994707868322855741819905904417953092666699096963112
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 作法:使用delegatecall時必須牢記兩件事
- delegatecall保留context(storages、caller等) → 執行後改變的是呼叫delegatecall的合約狀態
- 呼叫delegatecall的合約 & 被呼叫的合約 之 storage layout須相同
- 過程:A.attack → H.fallback → H.delegatecall → L.pwn → H.owner = msg.sender (i.e., A.address)
- 修正:使用不會改變state variable的library
- 補充:delegatecall得小心使用,用法錯誤或理解不正確可能導致毀滅性的結果
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 作法:利用Attack合約進行相同的計算產出answer,接著呼叫G.guess()將answer傳送過去即破解
- 過程:A.attack → compute answer → G.guess(answer) → G send ether to A
- 修正:實作與隨機數有關的應用時,不可使用blockhash與block.timestamp
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 作法:利用DOS原理(i.e., revert),使得合約變得無法再使用 (unusable)
- 過程:User1 sends 1 ether to K.claimThrone → User2 sends 1 ether to K.claimThrone → User1 receives 1 ether (refund) → A.attack [+ 3 ethers] → K.claimThrone → 其他users無法再執行K.claimThrone
- 修正:改用withdraw的方式,讓users自行領取refunds (favor pull over push)
- 補充:下一位user執行K.claimThrone並要refund給A時,因為該合約沒有fallback function,故發生revert而無法順利refund,導致current King無法退位使得合約becomes unusable。
- 補充:Attack合約內的comments描述「即便沒有require(sent, …),此攻擊仍然有效」,但自行測試結果表示攻擊無效[??],僅導致變數值(balance)與合約餘額(K.balance)變得不同
- 其他:
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 作法:利用tx.origin的特性 + 網路釣魚 以實現攻擊
- 過程:Attacker誘使W合約owner呼叫A.attack → W.transfer → tx.origin = owner → 成功將ethers轉給Attacker
- 修正:改用msg.sender而不使用tx.origin
- 補充:若合約A呼叫合約B、合約B呼叫合約C,在C中msg.sender是B,tx.origin是A
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 作法:任何地址都可以被鑄造成特定的合約,即使該地址的合約不是被鑄造的那個
- 過程:佈署Foo合約時,_bar填寫的是Mal合約(layout與Bar相同)地址,Mal為其他檔案且被隱瞞 → 呼叫F.callBar() → 結果執行的是M.log [user以為是執行Bar.log] → M.log裡可能包含惡意程式碼
- 修正:於F.constrcutor產生新合約[bar = new Bar()]、並且將bar改為public以方便瀏覽合約
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 作法:Attacker可以監視tx pool並發送交易,將其包含在user交易之前的塊中。這種機制可以被用來重新排序交易,以達到對attacker有利。
- 過程:user找到正確solution後發送交易呼叫F.solve → A在tx pool中發現user交易中的答案 → A發送交易(+找到的答案 & 設定高額gas price) → A的交易被排在user前面 → A獲得獎金10 ether
- 修正:採用 commit-reveal scheme 寫法、或者是使用 submarine send
- Commit-Reveal Schemes為一種加密演算法,用於允許某人承諾一個值,同時將其隱藏不讓其他人知道,並能夠在以後揭示它。該方案有兩個階段:選擇和指定值的commit phase & 顯示和檢查值的reveal phase
- 隱藏committed value的方法即使用hash;要reveal時,將solution之hash與committed value比對以確認commit前後答案是否一致、再與正確答案(bytes32 public hash)比對即可
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 作法:block.timestamp 可以被礦工操縱
- 過程:Attacker運行一個powerful miner節點以控制block timestamp → 該節點設定一個可以被15整除的timestamp並出塊 → attacker贏得這場遊戲並取得ethers
- 修正:不要使用 block.timestamp 作為隨機數
- 補充:礦工實際上可以在區塊被驗證後的15秒內發佈一個timestamp (from Consensys)
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 作法:相同簽名可多次被使用來執行函數。若簽名者的意圖是一次批准交易,這可能是有害的。
- 修正:加入nonce和合約address簽署訊息 & 記錄txHash是否已執行 (或導入EIP-712實作方法)
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- 作法:佈署一份新合約,使之在佈署時constructor內呼叫該對象合約即能繞過size check
- 補充:使用extcodesize判斷caller是否為合約未能 100% cover (在constructor內呼叫時,其extcodesize = 0)
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
Oracle Manipulation
https://consensys.github.io/smart-contract-best-practices/attacks/oracle-manipulation/
Griefing
https://consensys.github.io/smart-contract-best-practices/attacks/griefing/
Honeypot
https://solidity-by-example.org/hacks/honeypot/