Vụ hack 600 triệu đô của Poly Network ngày 11/8 đã làm rúng động toàn bộ thị trường Crypto.
Vụ hack xảy ra như thế nào?
Poly Network có một contract tên là EthCrossChainManager. Đây chính à contract có quyền trigger message từ chain khác, trong kiến trúc của các dự án cross-chain, có thể coi đây chính là người điểu khiển của hệ thống.
Có một hàm tên là verifyHeaderAndExecuteTx
nơi mọi người gọi đến để thực hiện cross-chain transaction. Về cơ bản nó có 2 nhiệm vụ:
function verifyHeaderAndExecuteTx(
bytes memory proof,
bytes memory rawHeader,
bytes memory headerProof,
bytes memory curRawHeader,
bytes memory headerSig
) whenNotPaused public returns (bool)
Cuối cùng nó gọi hàm _executeCrossChainTx
, trên target contract. Đây chính là lỗ hổng mấu chốt của vụ tấn công.
function _executeCrossChainTx(
address _toContract,
bytes memory _method,
bytes memory _args,
bytes memory _fromContractAddr,
uint64 _fromChainId
) internal returns (bool)
Tất nhiên để tránh lỗi, Poly Network có kiểm tra xem target có phải contract hay không bằng hàm này:
require(Utils.isContract(_toContract), "The passed in address is not a contract!");
Nhưng họ quên phải loại trừ viêc target là một contract vô cùng quan trọng – chính là contract EthCrossChainData
.
Tại sao điều này quan trọng đến vậy? EthCrossChainData
lưu giữ public key của account giữ tiền – gọi là Keeper
. Nếu public key này bị thay đổi, thì đồng nghĩa với việc giao tiền cho người khác giữ. Mục tiêu của hacker chính là bằng cách nào đó thay đổi public key này thành public key của account của mình, chuyển mình thành Keeper
.
bytes public ConKeepersPkBytes;
Cách duy nhất có thể thay đổi public key của Keeper
chính là bởi owner của contract EthCrossChainData
, và không ai khác, đó chính là người điều khiển, contract EthCrossChainManager
function putCurEpochConPubKeyBytes(
bytes memory curEpochPkBytes
) public whenNotPaused onlyOwner returns (bool) {
ConKeepersPkBytes = curEpochPkBytes;
return true;
}
Như vậy bằng cách nào đó, ta có thể từ contract EthCrossChainManager
gọi đến EthCrossChainData
contract, nghiễm nhiên ta đã pass qua được bước kiểm onlyOwner
check. Giờ chỉ còn lại việc gọi làm sao cho đúng hàm putCurEpochConPubKeyBytes
thôi là có thể thay đổi được public key rồi.
Quay trở lại bước 3 bên trên, ta có thể thực hiện hàm _executeCrossChainTx
với target bất kì, rỏ ràng đây chính là gợi ý cho ta để hiện thực hóa việc gọi hàm putCurEpochConPubKeyBytes
từ contract EthCrossChainData
.
Có một điểm khó ở đây là, contract EthCrossChainManager
chỉ chấp nhận method có dạng _method(bytes, bytes, uint64)
trong đó _method
là tên hàm tự định nghĩa
(success, returnData) = _toContract.call(abi.encodePacked(bytes4(keccak256(abi.encodePacked(_method, "(bytes,bytes,uint64)"))), abi.encode(_args, _fromContractAddr, _fromChainId)));
hàm này khác với hàm putCurEpochConPubKeyBytes(bytes)
bên trên. Làm sao ta có thể gọi được?
Ta biết rằng trong solidity, mỗi khi contract gọi hàm, thì thực chất là nó gọi đến signature hash
, tức 4 bytes đầu tiên của giá trị hash của tên hàm kèm tham số <function name>(<function input types>)
. Ví dụ sighash của hàm transfer trong ERC20 function chính là 4 bytes đầu tiên của giá trị hash của transfer(address,uint256)
.
Hàm _executeCrossChainTx
chỉ nhận method có dạng _method(bytes, bytes, uint64)
, nhưng điểm chết người ở đây chính là _method
là tên phương thức tự định nghĩa; vậy nên kẻ tấn công hoàn toàn có thể brute force hàng loạt các method để có thể tạo ra được giá trị hash trùng 4 bytes đầu với giá trị hash của putCurEpochConPubKeyBytes(bytes)
. Điều này là hoàn toàn khả thi vì sighash chỉ có 4 bytes mà thôi.
(success, returnData) = _toContract.call(abi.encodePacked(bytes4(keccak256(abi.encodePacked(_method, "(bytes,bytes,uint64)"))), abi.encode(_args, _fromContractAddr, _fromChainId)));
Và kẻ tấn công đã tìm ra được một method phù hợp
– method gốc
>http://ethers.utils.id('putCurEpochConPubKeyBytes(bytes)').slice(0, 10)
'0x41973cd9'
– method của hacker
> http://ethers.utils.id('f1121318093(bytes,bytes,uint64)').slice(0, 10)
'0x41973cd9'
Tuyệt vời! Không cần phải lộ key! Chỉ đơn giản là tìm ra một hash collision để tấn công vào contract chứa hàng trăm triệu đô.
Vụ hack khá dễ hiểu, nhưng phải nói để nghĩ ra được kịch bản hash collision này thì hacker quá đỗi thông minh.
Respect!
Nguồn : https://kiendt.me/2021/08/11/poly-network/
You need to login in order to like this post: click here
YOU MIGHT ALSO LIKE