Bởi: Kong@ Đội bảo mật Slow Mist
lý lịch
Vào ngày 9 tháng 6, Optimism và Wintermute đều đưa ra thông báo, tiết lộ cho cộng đồng một sự cố trong đó 20 triệu mã thông báo OP đã bị mất. Sự lạc quan đã ủy quyền cho Wintermute cung cấp dịch vụ thanh khoản cho OP trên thị trường thứ cấp và sẽ cung cấp 20 triệu mã thông báo OP cho Wintermute. Để nhận được mã thông báo này, Wintermute đã cung cấp cho Optimism một địa chỉ đa chữ ký Sau khi Optimism gửi hai giao dịch trong thử nghiệm và Wintermute xác nhận rằng chúng là chính xác, Optimism đã chuyển 20 triệu OP đến địa chỉ này. Sau khi Optimism chuyển mã thông báo, Wintermute nhận thấy rằng họ không có cách nào để kiểm soát các mã thông báo này, vì địa chỉ đa chữ ký mà họ cung cấp chỉ được triển khai trên mạng chính Ethereum vào thời điểm hiện tại và chưa được triển khai cho mạng Optimism. Wintermute ngay lập tức triển khai hoạt động khắc phục, nhưng kẻ tấn công đã nhận thấy lỗ hổng này và triển khai đa chữ ký đến địa chỉ của mạng Optimism trước Wintermute, kiểm soát thành công 20 triệu mã thông báo. Vậy câu hỏi đặt ra là tại sao lại có kẽ hở như vậy?
kiến thức trước
Trước hết cần xác định xem chữ ký giao dịch có tuân theo tiêu chuẩn [EIP155] hay không, chữ ký tuân theo tiêu chuẩn [EIP155] sẽ băm 9 phần tử mã hóa RLP (nonce, gasprice, gas, to, value, data, chainid , 0, 0), trong đó Chainid được bao gồm, vì vậy giá trị v của chữ ký tuân thủ [EIP155] là {0,1} + chainid * 2 + 35. Đối với chữ ký không theo chuẩn [EIP155] chỉ băm 6 phần tử (nonce, gasprice, gas, to, value, data) nên giá trị của v sau khi ký là {0,1} + 27. Các chuỗi khác nhau sẽ xác định các chuỗi khác nhau và các chuỗi khác nhau sẽ nhận được các giá trị v khác nhau. Theo ECDSA, chúng tôi biết rằng khi giá trị của v khác nhau, ngay cả khi giá trị của r và s giống nhau, thì khóa công khai được khôi phục bằng chữ ký cũng khác nhau. Do đó, các giao dịch đáp ứng tiêu chuẩn [EIP155] không thể được phát lại thành công trên các chuỗi khác.
Điều đáng nói là [EIP2718] đã giới thiệu một định dạng giao dịch mới 0x02 || RLP([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit,destination,mount,data,access_list, signature_y_parity, signature_r, signature_s]), chainid được mã hóa riêng và không được bao gồm trong giá trị chữ ký v. Giá trị chữ ký v chỉ được sử dụng như một bit chẵn lẻ đơn giản, vì vậy giá trị v thu được bằng chữ ký giao dịch hiện tại trở thành 0 hoặc 1.
phát lại giao dịch
Sau khi hiểu cấu trúc chữ ký giao dịch ở trên, chúng ta có thể biết rõ rằng giá trị chữ ký v là 27 hoặc 28 có thể được phát lại trên các chuỗi khác nhau. Vậy làm thế nào để phát lại trên các chuỗi khác nhau? Điều này không khác gì gửi giao dịch, chúng ta chỉ cần gửi nội dung giao dịch ban đầu trên các chuỗi khác.
Lấy ví dụ về vụ đánh cắp 20 triệu mã thông báo OP trong Wintermute, trong đó kẻ tấn công đã thực hiện lại giao dịch trong đó Gnosis Safe triển khai hợp đồng Factory. Ở đây, chúng tôi cố gắng phát lại các giao dịch Gnosis Safe Deployer 3 với nonce là 3.
Một phương pháp đơn giản hơn là trước tiên lấy giao dịch ban đầu thông qua Etherscan:
Sau đó trực tiếp thông qua eth_sendRawTransaction [RPC] của Optimistic
(https://eth.wiki/json-rpc/API) để gửi.
Nếu không thể lấy trực tiếp nội dung giao dịch ban đầu, trước tiên chúng tôi có thể chuyển eth_getTransactionByHash
[RPC](https://eth.wiki/json-rpc/API) để lấy nội dung giao dịch.
Sau đó, RLP mã hóa nội dung giao dịch để lấy nội dung giao dịch ban đầu:
Sau đó vượt qua eth_sendRawTransaction [RPC] của Optimistic
(https://eth.wiki/json-rpc/API) để gửi.
Tham khảo :
https://eips.ethereum.org/EIPS/eip-155
https://eips.ethereum.org/EIPS/eip-2718
https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm
https://github.com/ethereum/go-ethereum/blob/master/core/types/transaction_signing.go