前言

隨着電視劇的爆火,魷魚遊戲的熱度也逐漸攀升,而 SQUID 在 10 月 21 日上線後,價格幅度變化巨大,但是現在的它真的安全嗎?

Squid 從代碼上來看其依賴於以太坊提案 EIP-1967,知道創宇區塊鏈安全實驗室將從 EIP-1967 提案再談 Squid 的安全風險。

SQUID 價格從 0.08 刀一路扶搖直上九萬里,飆升到 3000 美元。然而令人唏噓的是,價格隨即閃崩,5 分鐘從 3000 跌至 0.0007,究其原因,合約開發者在短短的幾分鐘內利用 7000w 枚 Squid 砸盤 ,導致流動性池被抽乾,直至歸零。
在經歷了底部 0.0007 後,該遊戲代幣最近短短几天重新漲到了 0.4,漲幅達到了 50 倍以上,可謂勢頭不小。
知道創宇區塊鏈安全實驗室 | 警惕!從 EIP-1967 再談 “魷魚遊戲 SQUID” 安全風險

EIP-1967

This EIP standardises the storage slot for the logic contract address, instead of a public method on the proxy contract as DelegateProxy (EIP897) does. The rationale for this is that proxies should never expose functions to end users that could potentially clash with those of the logic contract.

首先,EIP-1967 的目的是規定一個通用的存儲插槽,用於在代理合約中的特定位置存放邏輯合約的地址。
在該提案中,對邏輯合約地址的存儲槽進行了標準化,而不是像 DelegateProxy (EIP897) 一樣在代理合約上使用公共方法。
通過跟進該提案,在提案中定義瞭如下的標準化存儲槽。

bytes32(uint256(keccak256(\"eip1967.proxy.implementation\")) - 1)
bytes32(uint256(keccak256(\"eip1967.proxy.beacon\")) - 1)
bytes32(uint256(keccak256(\"eip1967.proxy.admin\")) - 1)

最後通過計算,列出來了三個 byte32 位 的 Hash 值作爲推薦的存儲槽。

bytes32(uint256(keccak256(\"eip1967.proxy.implementation\")) - 1)
// 邏輯合約地址 存儲槽
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc

bytes32(uint256(keccak256(\"eip1967.proxy.beacon\")) - 1)
// 信標合約地址 存儲槽
0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50

bytes32(uint256(keccak256(\"eip1967.proxy.admin\")) - 1)
//admin 地址 存儲槽
0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103

知道創宇區塊鏈安全實驗室 | 警惕!從 EIP-1967 再談 “魷魚遊戲 SQUID” 安全風險
EIP-1967 中有提到,對於一個代理來說,代理永遠不應該向最終用戶公開可能與邏輯合約功能衝突的方法。此處計算將 keccak256 結果進行減一再轉化爲 bytes32 來隱藏前像,以防止基於函數簽名攻擊。

針對該攻擊,具體哈希後的前 4 個 bytes 函數簽名的攻擊思路如下:

由於 solidity 中識別一個函數,靠的是函數簽名,而函數簽名是函數哈希後的前 4 個 bytes,是非常容易碰撞出來的。而函數簽名相關知識點可以參考實驗室之前的 文章

在一個獨立的 solidity 文件中,編譯器自己會去檢查所有的 external 和 public 函數是否存在函數簽名碰撞,但對於代理模式的合約文件,可能存在 proxy 合約中的函數簽名與 impl 合約中的函數簽名碰撞。而一旦發生這種碰撞,proxy 合約中的函數就會被直接調用,而不是 impl 合約對應的函數。

除了以上用途,使用 EIP-1967 還有如下的好處

合約代理模式規範性: 
由於代理合約中缺少用於獲取代理的邏輯地址的通用接口,因此無法構建對這些信息進行操作的通用工具。比如在區塊鏈瀏覽器中,最終用戶想要與底層邏輯合約而不是代理本身進行交互。

如果擁有從代理檢索邏輯合約地址的通用方法將可以允許區塊瀏覽器去顯示邏輯合約的 ABI 而不是代理的 ABI。瀏覽器可以還檢查合約在不同槽位的存儲,來確定它是否確實是一個代理。

Squid 安全性

通過跟進 Squid 合約,雖然我們看到合約部署者 0x87230146e138d3f296a9a77e497a2a83012e9bc5 通過 renounceOwnership 方法將 _owner 設置爲了 0 地址:
知道創宇區塊鏈安全實驗室 | 警惕!從 EIP-1967 再談 “魷魚遊戲 SQUID” 安全風險
知道創宇區塊鏈安全實驗室 | 警惕!從 EIP-1967 再談 “魷魚遊戲 SQUID” 安全風險
但是這樣就可以高枕無憂了嗎?在 Squid 的初始化中,部署者通過爲ApprovedEngine傳入邏輯函數地址並設置爲接口實現。 
知道創宇區塊鏈安全實驗室 | 警惕!從 EIP-1967 再談 “魷魚遊戲 SQUID” 安全風險
但與此同時,初始化傳入了參數 _sir,該參數從代碼中可以發現,其被存儲在了存儲槽 (storage[\'0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103\']) 中。該地址後續可成功通過 ifSir 修飾器的檢查並隨時調用 approveTo 函數來部署新的邏輯合約實現。
知道創宇區塊鏈安全實驗室 | 警惕!從 EIP-1967 再談 “魷魚遊戲 SQUID” 安全風險
_sir 可通過升級對應的邏輯合約,進而改變了 _implementation 返回的對於 EIP-1967 中提到的槽中 bytes32(uint256(keccak256(\"eip1967.proxy.implementation\") - 1)) 的值通過改變 Engine 收到調用的時候合約執行邏輯,進而修改邏輯合約的代碼:
知道創宇區塊鏈安全實驗室 | 警惕!從 EIP-1967 再談 “魷魚遊戲 SQUID” 安全風險
針對該修改邏輯合約進行攻擊已屢見不鮮,在幾天前 bzx 就曾因 \” 開發方被釣魚導致私鑰被盜 \”,進而代理合約被修改並影響到了流動性提供者,攻擊者不僅轉走了原本合約內的資金,還嘗試轉移對合約進行了 approve 的賬戶的資產。

事件參考(https://bzx.network/blog/prelminary-post-mortem

最後,通過追溯該 Squid 的部署合約,我們可以發現該合約的具體部署
Transaction:https://bscscan.com/tx/0xcaa4186f7e4dacd856835cb92a8518c88b098a33348a42168f99a0c57b7291c7

而在此 Transaction 中,傳入合約的參數最後一個參數則爲_sir 地址:
0x6bdb3b0fd9f39427a07b8ab33bac32db67eb4e38
知道創宇區塊鏈安全實驗室 | 警惕!從 EIP-1967 再談 “魷魚遊戲 SQUID” 安全風險
當然,還存在多種方法拿到該地址如:
知道創宇區塊鏈安全實驗室 | 警惕!從 EIP-1967 再談 “魷魚遊戲 SQUID” 安全風險
並且該 sir 後門地址至今仍非常活躍:
知道創宇區塊鏈安全實驗室 | 警惕!從 EIP-1967 再談 “魷魚遊戲 SQUID” 安全風險

後記

Squid 經過了暴跌與暴漲,但本質上仍沒有完成去中心化,合約依然存在諸多安全風險。在頻繁和大幅度的漲跌中,常有參與者忘記了本心,最後導致虧空的結局。

實驗室官網:www.knownseclab.com
知道創宇唯一指定存證平臺www.attest.im
聯繫我們:[email protected]

知道創宇區塊鏈安全實驗室導航

微信公衆號
@ 創宇區塊鏈安全實驗室
知道創宇區塊鏈安全實驗室 | 區塊鏈安全解決方案:讓人類進入安全的區塊鏈世界
官方網站
@ 知道創宇區塊鏈安全實驗室
知道創宇區塊鏈安全實驗室 | 區塊鏈安全解決方案:讓人類進入安全的區塊鏈世界
微博
@ 知道創宇區塊鏈實驗室
https://weibo.com/BlockchainLab

知乎
@ 知道創宇區塊鏈安全實驗室
https://www.zhihu.com/org/zhi-dao-chuang-yu-qu-kuai-lian-an-quan-shi-yan-shi

Twitter
@KS_Blockchain
https://twitter.com/KSBlockchain
知道創宇區塊鏈安全實驗室 | 區塊鏈威脅情報中心正式開放使用