Tiêu đề gốc: " Tiết lộ lỗ hổng bẻ khóa khóa riêng của Ethereum Vanity Generator 》
Tác giả gốc: Johan, Nhóm bảo mật SlowMist
Gần đây, ví Wintermute đã bị tấn công và mất đi khoảng 160 triệu USD. Nguyên nhân vụ trộm là để tiết kiệm phí gas, Wintermute đã sử dụng Profanity để tạo ra ví Vanity (bắt đầu bằng 0x0000000), sau khi công ty tổng hợp nền tảng giao dịch phi tập trung 1inch phát hành một báo cáo tiết lộ bảo mật tuyên bố rằng một số địa chỉ Ethereum nhất định được tạo thông qua một công cụ có tên là Profanity có lỗ hổng nghiêm trọng. Nhóm bảo mật SlowMist đã tiến hành phân tích chuyên sâu về sự cố này và chia sẻ với mọi người.

Mật mã đường cong Elliptic (ECC) là loại mã hóa được sử dụng phổ biến nhất trong chuỗi khối Thuật toán trường, ECC là một danh mục lớn các thuật toán mã hóa, bao gồm nhiều đường cong và thuật toán mã hóa khác nhau, chẳng hạn như secp256k1/secp256r1/ed25519/schnorr, v.v. Cả Bitcoin và Ethereum đều sử dụng mã hóa secp256k1.

Khi sử dụng Ethereum , chúng tôi đầu tiên sẽ tạo một tài khoản riêng (bắt đầu bằng 0x + 40 chữ cái), quy trình cụ thể như sau:
(1) Tạo hạt giống số ngẫu nhiên không thể đoán trước, Thông thường dựa trên trình tạo ngẫu nhiên /dev/urandom của hệ thống;
(2) Sử dụng hạt giống ngẫu nhiên để tạo khóa riêng (256 bit, 32 byte)
(3) Tạo khóa chung (64 byte) từ khóa riêng
(4) Sử dụng khóa chung thuật toán băm keccak-256, lấy 40 chữ cái cuối cùng của chuỗi thập lục phân của giá trị băm và thêm 0x vào đầu để tạo địa chỉ Ethereum cuối cùng.
Đặc biệt chú ý đến kích thước của khóa công khai và khóa riêng này, vì nó liên quan đến khối lượng tính toán mà chúng ta sẽ thực hiện sau này.
Ở đây cần nhắc đến một công thức quan trọng:
Q = kG
p>
Đây là thuật toán cốt lõi để lấy khóa chung từ khóa riêng, việc tính toán lấy khóa chung từ khóa riêng rất đơn giản nhưng đạo hàm ngược lại là gần như không thể.
Q là khóa chung, k là khóa riêng, k gồm các số nguyên lớn trong trường hữu hạn, lớn đến mức gần như không thể đoán được, G nằm trên Đường cong elip Một điểm theo mặc định là một giá trị cố định và kG là phép cộng của k G điểm (cộng điểm của đường cong elip không phải là phép cộng đơn giản của các số thực và phương pháp tính toán sẽ không được thảo luận ở đây).
Nếu chúng ta muốn tìm kiếm kỹ lưỡng các tài khoản Ethereum và tìm khóa riêng của Vitalik thì sau khi biết khóa chung của anh ta, chúng ta cần thực hiện tối đa 2^256 (2 256 power) nhân Q = kG phép tính, chúng ta đương nhiên không nhạy cảm với số lượng lớn nên đã quy đổi thành khối lượng công việc, tức là tốc độ hiện tại của máy tính Apple M1 là khoảng 40M/s, thì số năm ước chừng cần thiết là 8 theo sau là 62 số 0. vạn năm quá dài, hãy chiến đấu ngay bây giờ.
Có một số phương pháp tạo khóa riêng sai, sẽ khiến phạm vi giá trị của khóa riêng thay đổi thành phạm vi giá trị nhỏ hơn và trở nên dễ đoán. Các lý do phổ biến là:
(1) Hạt giống số ngẫu nhiên không đủ ngẫu nhiên. Ví dụ: nếu dấu thời gian được sử dụng làm hạt giống số ngẫu nhiên thì chúng tôi chỉ cần liệt kê đầy đủ khoảng thời gian đã qua. Tất cả dấu thời gian có thể được sử dụng để tìm hạt giống và khóa riêng được sử dụng để tạo khóa chung;
(2) Phần mềm thuật toán có sai sót, dẫn đến độ mạnh ngẫu nhiên không đủ (Tục tĩu là Có một lỗ hổng như vậy);
Thô tục được thiết kế để giúp mọi người tạo tài khoản với hiệu ứng hình ảnh đặc biệt, chẳng hạn như tài khoản bắt đầu hoặc kết thúc bằng một ký tự đặc biệt. Mặt khác, một số nhà phát triển sử dụng nó để tạo các tài khoản bắt đầu bằng nhiều số 0, chẳng hạn như 0x00000000ae347930bd1e7b0f35588b92280f9e75, có thể tiết kiệm Gas khi gọi hợp đồng thông minh.
Để phát tán Địa chỉ ảo nhanh hơn, Profanity chỉ lấy một số ngẫu nhiên một lần khi bắt đầu chương trình và tất cả các khóa riêng tiếp theo đều được lặp lại dựa trên số ngẫu nhiên này. Mở rộng, chúng ta hãy xem thuật toán tạo số ngẫu nhiên của nó:

random_device được sử dụng ở đây để lấy số ngẫu nhiên do hệ thống cung cấp. Nguồn số ngẫu nhiên này đáp ứng cường độ cần thiết cho mã hóa. Nhưng khi để ý đến loại biến, chúng tôi thấy rằng rd() trả về một giá trị ngẫu nhiên có độ dài 32 bit, như chúng tôi đã đề cập ở trên, khóa riêng có chiều dài 256 bit nên quá trình lấy số ngẫu nhiên trong một lần không thể điền vào nó Toàn bộ khóa riêng, vì vậy Profanity sử dụng mt19937_64 để tạo các số ngẫu nhiên để điền vào toàn bộ khóa riêng. Có sự khác biệt cơ bản giữa các thuật toán ngẫu nhiên của mt19937_64 và Random_device . mt19937_64 có tính xác định. Tính ngẫu nhiên của nó phụ thuộc vào số ngẫu nhiên đầu vào và không tạo ra tính ngẫu nhiên mới.
Nghĩa là, nếu giá trị được truyền cho rd() nằm trong một phạm vi nhất định thì giá trị của distr(eng) cũng sẽ ở trong phạm vi tương ứng phạm vi, giá trị r được hàm createSeed trả về đương nhiên cũng nằm trong một phạm vi nhất định.
Điểm mấu chốt ở đây: tất cả các khả năng của rd() là 2^32, khác xa so với tính bảo mật của khóa riêng (2^ 256) 224 bậc độ lớn, nhưng 2^32 cũng khá lớn, vậy cần bao nhiêu phép tính để bẻ khóa riêng?
Sau khi Profanity lấy được khóa riêng đầu tiên SeedPrivateKey, để va chạm với địa chỉ tài khoản được yêu cầu, nó sẽ liên tục cập nhật khóa riêng thông qua một thuật toán cố định. 2 triệu lần (giá trị đến từ bài viết được tiết lộ bởi 1inch), cách tính public key này có thể biểu thị như sau:
PublicKey = kG = (SeedPrivateKey + Iterator) *G
Iterator là một số tăng dần, khi biết PublicKey ta có thể lấy SeedPrivateKey bằng cách liệt kê đầy đủ SeedPrivateKey và Iterator. Số lượng tính toán khoảng 2 ^ Nhân 32 nhân 2 triệu lần, trên chiếc máy tính M1 phải hơn 60 năm, coi như cuộc đời này còn có hy vọng :D. Nếu tôi sử dụng số lượng lớn card đồ họa có sức mạnh tính toán lớn hơn để thực hiện các phép tính song song thì hoàn toàn có thể cho ra kết quả như mong muốn trong vòng vài ngày, thậm chí vài giờ.
Ethereum gần đây đã chuyển sang cơ chế đồng thuận PoS và có rất nhiều sức mạnh tính toán của card đồ họa nhàn rỗi, nếu thợ mỏ sử dụng card đồ họa để bẻ khóa khóa riêng, điều đó sẽ xảy ra sẽ không thể thực hiện được trong vài phút. Tất nhiên thuyết âm mưu này chẳng có ý nghĩa gì, chúng tôi chỉ muốn nghiên cứu khả năng phá vỡ nó mà thôi. Chúng tôi muốn một phương pháp ít bạo lực hơn để mở khóa khóa riêng.
Chúng ta dịch chuyển cả hai vế của phương trình một chút và biến đổi công thức trên, ta có:
SeedPrivateKey*G = PublicKey - Iterator*G
Chúng ta có thể nghĩ về một phương thức tấn công khác. Nếu SeedPrivateKey*G được tính toán trước lần đầu tiên, nó sẽ cần tới 256 G. Dung lượng bộ nhớ để lưu trữ kết quả tính toán hoàn toàn có thể đạt được trên một máy chủ thông thường, số lần tính toán yêu cầu là 2^32 lần, có thể hoàn thành trong khoảng chục phút. Sau đó ta thay PublicKey cần crack vào vế phải của phương trình rồi thực hiện va chạm trên Iterator, lượng tính toán cần khoảng 2 triệu lần, có 2 triệu bảng tra cứu, thời gian cần là giây . Điều này thật thú vị, hóa ra một số ngẫu nhiên 32 bit không đáng kể đến mức bất kỳ ai cũng có thể khôi phục khóa riêng trong vòng hàng chục phút.
Tại thời điểm này, chúng tôi đã kết luận rằng nguyên nhân gây ra lỗ hổng Profanity là do khóa riêng 256-bit không được gieo đủ ngẫu nhiên, dẫn đến phạm vi giá trị bị giảm nghiêm trọng của khóa riêng. Đồng thời, chúng tôi cũng phân tích khả năng giải được loại bài toán ngẫu nhiên này, hy vọng mang lại cho bạn chút cảm hứng.
Liên kết gốc
Chào mừng bạn tham gia cộng đồng chính thức của BlockBeats:
Nhóm Telegram đăng ký: https://t.me/theblockbeats
Nhóm Telegram thảo luận: https://t.me/BlockBeats_App
Tài khoản Twitter chính thức: https://twitter.com/BlockBeatsAsia