以太坊智慧合約call注入攻擊
這是我在先知安全大會上分享議題中的一部分內容。主要介紹了利用對call呼叫處理不當,配合一定的應用場景的一種攻擊手段。
0x00 基礎知識
以太坊中跨合約呼叫是指的合約呼叫另外一個合約方法的方式。為了好理解整個呼叫的過程,我們可以簡單將呼叫發起方合約當做傳統web世界的瀏覽器,被呼叫的合約看作webserver,而呼叫的msg則是http資料,EVM底層通過ABI規範來解碼引數,獲取方法選擇器,然後執行對應的合約程式碼。
當然,實際上智慧合約的執行一般在打包交易或者驗證交易的時候發生,上面的比喻只是方便理解。
在solidity語言中,我們可以通過call方法來實現對某個合約或者本地合約的某個方法進行呼叫。
呼叫的方式大致如下:
<address>.call(方法選擇器, arg1, arg2, …)
<address>.call(bytes)
如上所述,可以通過傳遞引數的方式,將方法選擇器、引數進行傳遞,也可以直接傳入一個位元組陣列,當然要自己去構造msg.data的結構。
Solidity程式設計中,一般跨合約呼叫執行方都會使用msg.sender全域性變數來獲取呼叫方的以太坊地址,從而進行一些邏輯判斷等。
比如在ERC20標準中的transfer方法的實現中,就是使用msg.sender來作為扣款方:
function transfer(address _to, uint256_value) returns (bool success) {
….
balances[msg.sender]-= _value;
balances[_to] += _value;
….
}
0x01 攻擊模型
Call方法注入漏洞,顧名思義就是外界可以直接控制合約中的call方法呼叫的引數,按照注入位置可以分為以下三個場景:
1. 引數列表可控
<address>.call(bytes4 selection, arg1, arg2, ...)
2. 方法選擇器可控
<address>.call(bytes4selection, arg1, arg2, ...)
3. Bytes可控
<address>.call(bytesdata)
<address>.call(msg.data)
簡單舉個例子,比如存在一個合約B,程式碼如下:
contract B{
function info(bytes data){
this.call(data) ;
}
function secret() public{
require(this ==msg.sender);
// secret operations
}
}
其中有info和secret方法,secret方法中判斷必須是合約自身呼叫才能執行。然而這裡的info方法中有個call的呼叫,並且外界可以直接控制call呼叫的位元組陣列,因此如果外界精心構造一個data,這個data的方法選擇器指定為secret方法,那麼外部使用者就可以以合約身份呼叫到這個secret方法,這樣就會造成一定的風險。
0x02 具體場景
這裡舉兩種實際的攻擊場景:
(1) bytes注入
在合約程式碼中,有個approveAndCallcode方法,這個方法中允許呼叫_spender
合約的某些方法或者傳遞一些資料,通過引入了_spender.call
來完成這個功能。
如果外界呼叫中指定_spender
為合約自身的地址,就可以以合約的身份去呼叫合約中的某些方法。比如如果我們使用合約的身份去呼叫transfer方法:
只需要自己去構造bytes即可,比如把transfer的_to
引數指定為我們自己的賬戶地址。這樣其實就可以直接把合約賬戶中的代幣全部轉到自己的賬戶中,因為通過call注入,在transfer方法看來,msg.sender其實就是合約自己的地址。
(2) 方法選擇器注入
比如這裡有個logAndCall方法:
function logAndCall(address _to, uint _value, bytes data, string_fallback){
…..
assert(_to.call(bytes4(keccak256(_fallback)),msg.sender, _value, _data)) ;
……
}
這裡我們對_fallback引數可控,也就是說我們可以指定呼叫_to地址的任何方法,但是後面跟了三個引數,分別是msg.sender
,_value
, _data
,型別分別為address,uint256以及bytes。那麼我們是不是隻能呼叫引數型別必須為這三個的方法呢?當然不是。這裡涉及到EVM在處理calldata的一個特性。
比如Sample1合約中有個test方法,這個方法中有三個引數,都是uint256型別的。而Sample2通過call呼叫了Sample1的test方法,這裡傳入了5個引數,同樣是可以呼叫成功的。這是因為EVM在獲取引數的時候沒有引數個數校驗的過程,因此取到前三個引數1,2,3之後,就把4,5給截斷掉了,在編譯和執行階段都不會報錯。
利用這個特性,我們其實有很多攻擊面,比如我們可以通過logAndCall中的call注入來呼叫approve方法:
這裡的approve方法有兩個引數,而且型別為address和uint256,所以我們是可以呼叫成功的。這樣就可以將合約賬戶中的代幣授權給我們自己的賬戶了。
0x03 深遠的問題
ERC223標準是為了解決ERC20中對智慧合約賬戶進行轉幣場景缺失的問題,可以看作是ERC20標準的升級版。但是在很多ERC223標準的實現程式碼中就帶入了call注入的問題:
此外,很多合約在判斷許可權的時候會將合約自身的地址也納入到白名單中:
0x04 防護手段
針對本文提到的這個風險,作為開發者來說,需要對ERC223的實現進行排查,不要引入call注入問題,如果非要執行回撥,則可以指定方法選擇器字串,避免使用直接注入bytes的形式來進行call呼叫。對於一些敏感操作或者許可權判斷函式,則不要輕易將合約自身的賬戶地址作為可信的地址。
相關文章
- 以太坊蜜罐智慧合約分析
- 以太坊智慧合約升級策略
- 以太坊智慧合約-猜數字
- 以太坊智慧合約gas如何估計?
- 如何打造安全的以太坊智慧合約
- 技術工坊|深度探索以太坊智慧合約(深圳)
- 區塊鏈——以太坊、智慧合約簡介區塊鏈
- 以太坊智慧合約 Hexagon 存在溢位漏洞Go
- 以太坊智慧合約開發第二篇:理解以太坊相關概念
- 什麼是以太坊?什麼是智慧合約?
- eth以太坊智慧合約交易平臺開發
- 使用Remix編譯和部署以太坊智慧合約REM編譯
- 以太坊智慧合約開發第四篇:實現Hello World智慧合約
- 智慧合約安全之重入攻擊
- 區塊鏈2.0以太坊智慧合約solidity之helloworld區塊鏈Solid
- 使用truffle部署以太坊智慧合約到區塊鏈區塊鏈
- 【精通以太坊】——第九章 智慧合約安全
- 以太坊中如何獲取另外一個智慧合約部署的合約地址?
- 以太坊智慧合約開發第七篇:智慧合約與網頁互動網頁
- 區塊鏈安全 - 以太坊短地址攻擊區塊鏈
- 區塊鏈安全————以太坊短地址攻擊區塊鏈
- olidity語言開發以太坊智慧合約中的繼承繼承
- 區塊鏈之--2小時構建以太坊智慧合約區塊鏈
- 以太坊智慧合約開發第五篇:字串拼接—Solidity字串Solid
- 以太坊常見合約型別及其用途型別
- 智慧合約開發(3)—— 以太坊虛擬機器(EVM)基礎虛擬機
- 採用以太坊智慧合約技術的報名系統原始碼原始碼
- 以太坊教程:搭建環境、編寫編譯一個智慧合約編譯
- 區塊鏈100講:淺析以太坊網路智慧合約原理區塊鏈
- 【區塊鏈】實戰·以太坊智慧合約程式設計引導區塊鏈程式設計
- 富士通推出新技術檢測以太坊智慧合約漏洞
- 以太坊智慧合約開發第六篇:truffle開發框架框架
- 如何取消以太坊智慧合約授權,防止被黑客盜取Token?黑客
- 以太坊智慧合約開發環境搭建以及第一個Dapp開發環境APP
- Polygon馬蹄鏈在以太坊上的智慧合約開發應用Go
- Conflux與以太坊合約開發工具區別UX
- 第九課 如何除錯以太坊官網的智慧合約眾籌案例除錯
- 以太坊智慧合約開發第三篇:安裝節點工具Ganache