根據NUMEN鏈上監控顯示,Jan-15-2023 05:43:37 PM +UTC時間,Jarvis_Network項目遭到攻擊,損失663101個MATIC。
具體tx如下:
https://polygonscan.com/tx/0x0053490215baf541362fc78be0de98e3147f40223238d5b12512b3e26c0a2c2f

Jarvis Network閃電貸重入攻擊事件分析

根據調用棧分析,交易中涉及到很多代幣轉賬,所以調用棧很長,我們分析發現在調用過程中存在重入的邏輯。在重入的過程中發現重入前和重入後,對同一個合約的同一個函數調用,傳入參數也一樣,但是返回值差別很大。
重入前:1002157321772769944
重入後:10091002696492234934

Jarvis Network閃電貸重入攻擊事件分析

重入發生在remove_liquidity,這個函數在(https://polygonscan.com/address/0xfb6fe7802ba9290ef8b00ca16af4bc26eb663a28#code)這個合約中。 remove_liquidity這個函數在移除流動性的時候會把用戶添加的代幣返回給用戶,由於polygon和evm是同構鏈,所以在matic轉賬給合約的時候會進入到合約的重入。詳細分析調用棧中重入的部分。

Jarvis Network閃電貸重入攻擊事件分析

首先看getUnderlyingPrice(https://polygonscan.com/address/0xcc6aa628516bb46391b05b16e5058c877461cc76#code)上面是邏輯合約,但是不開源。

Jarvis Network閃電貸重入攻擊事件分析

Jarvis Network閃電貸重入攻擊事件分析

根據插槽得到v1=0xe7cea2f6d7b120174bf3a9bc98efaf1ff72c997d,wtoken=0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270。所以進入if分支,然後通過插槽查到_oracles[v1]=0xacf3e1c6f2d6ff12b8aee44413d6834774b3f7a3,繼續進入裡面的else分支後調用0xacf3e1c6f2d6ff12b8aee44413d6834774b3f7a3合約的getUnderlyingPrice函數。 0xacf3e1c6f2d6ff12b8aee44413d6834774b3f7a3對應的邏輯合約是0x3803527dcd92ac3e72a0a164db82734daba47fac,這個合約也沒有開源

Jarvis Network閃電貸重入攻擊事件分析

Jarvis Network閃電貸重入攻擊事件分析

這個是一些內置的計算,因為攻擊涉及到重入,考慮到一定是重入前和重入後的一些變量是轉賬發生之後計算的,這部分代碼不涉及到外部變量,所以問題應該不在這。除此之外還調用了0x9de。

Jarvis Network閃電貸重入攻擊事件分析

varg0是傳入的代幣地址,也就是0xe7cea2f6d7b120174bf3a9bc98efaf1ff72c997d。對應的_poolOf[v0]是0xfb6fe7802ba9290ef8b00ca16af4bc26eb663a28。進入這個合約分析get_virtual_price函數。對應代碼如下:

Jarvis Network閃電貸重入攻擊事件分析

self.token還是0xe7cea2f6d7b120174bf3a9bc98efaf1ff72c997d,除數是這個代幣的總發行量

Jarvis Network閃電貸重入攻擊事件分析

看到D會影響get_xcp的返回。因為重入發生在remove_liquidity,所以分析remove_liquidity函數。詳細代碼如下:

Jarvis Network閃電貸重入攻擊事件分析

調用前後get_virtual_price返回值變化。

Jarvis Network閃電貸重入攻擊事件分析

self.D的更改在轉賬之後。攻擊者在移除流動性的時候,matic轉移到攻擊者合約,回調fallback時候先查了一下0xe7cea2f6d7b120174bf3a9bc98efaf1ff72c997d價格,由於self.D更新在轉賬之後,所以導致之前的價格獲取錯誤。

移除流動性方法流程:1)銷毀用戶LP;2)發送給用戶質押資金;3)更新self.D。 

self.D用於價格計算,添加流動性時也對self.D更新,並且攻擊者此次流動性資金較大。根據self.D計算公式self.D = D - D * amount / total_supply,amount和total_supply近乎相等,正常計算時,self.D值會小很多。 

但由於攻擊者在2)進行了重入,並且比原始價格增高了10倍進行借貸,正是因為self.D值在添加流動性時增大,而移除流動性時未及時更新。 

這裡remove_liquidity 移除流動性方法雖然添加了@nonreentrant('lock')來防止重入該方法,但由於攻擊者重入進入其他合約借貸資金,這裡重入鎖並不能奏效。

總結:

本次攻擊主要是因為變量修改邏輯在外部調用之後,導致價格獲取出現異常,並且跨合約重入使得重入鎖不能奏效,NUMEN實驗室提醒項目方代碼要經過嚴格的安全審計,變量修改要放在外部調用之前並且價格獲取採用多數據源的方式,代碼邏輯遵循先判斷,後寫入變量,再進行外部調用的編碼規範(Checks-Effects-Interactions)會使項目更加安全穩定。 NUMEN擁有專業的安全團隊,為WEB3生態保駕護航