Penulis: enze

Editor: Liz

Pendahuluan

Baru-baru ini, perpustakaan kriptografi elliptic yang banyak digunakan dalam ekosistem JavaScript ditemukan memiliki kerentanan keamanan serius (GHSA-vjh7-7g9h-fjfh). Penyerang dapat mengekstrak kunci privat hanya dengan satu tanda tangan dengan membangun input tertentu, sehingga sepenuhnya mengendalikan aset digital atau kredensial identitas korban.

Penyebab mendasar dari kerentanan ini terletak pada cacat pemrosesan input non-standar dari perpustakaan elliptic, yang menyebabkan angka acak k dalam tanda tangan ECDSA mungkin terulang. Karena keamanan algoritma ECDSA sangat bergantung pada keunikan k, begitu k terulang, kunci privat dapat langsung diturunkan, menyebabkan risiko keamanan yang tidak dapat diubah.

Artikel ini akan menganalisis prinsip kerentanan, penyebab, dan dampaknya, serta memberikan saran perbaikan untuk membantu pengembang mengurangi risiko keamanan.

Latar belakang kerentanan

Perkenalan perpustakaan elliptic

Elliptic adalah perpustakaan kriptografi kurva elips yang banyak digunakan dalam ekosistem JavaScript, mendukung berbagai kurva (seperti secp256k1, ed25519). Perpustakaan ini digunakan oleh banyak dompet cryptocurrency, sistem otentikasi, aplikasi Web3, dan lain-lain.

Ruang lingkup dampak

  • Versi yang terpengaruh: elliptic <= 6.6.0

  • Kurva yang terpengaruh: secp256k1, ed25519, dll.

  • Situasi yang terpengaruh: semua aplikasi yang menerima input eksternal untuk melakukan tanda tangan ECDSA (terutama sistem yang menerima input eksternal untuk tanda tangan)

Jika aplikasi bergantung pada elliptic untuk tanda tangan ECDSA dan memungkinkan pengguna memberikan pesan yang tidak difilter sebagai konten tanda tangan, kemungkinan besar akan terpengaruh oleh kerentanan ini.

Prinsip Kerentanan

Angka acak k dalam tanda tangan ECDSA

Bayangkan Anda memiliki stempel yang unik (kunci privat), setiap kali Anda menandatangani dokumen, Anda perlu mengambil tinta yang unik (angka acak k). Jejak yang ditinggalkan akan menghasilkan dua angka r dan s, ini adalah hasil tanda tangan.

Proses ini memiliki beberapa poin kunci:

  • Stempel (kunci privat) selalu sama - mewakili identitas Anda.

  • Setiap tanda tangan harus menggunakan stempel baru (angka acak k tidak boleh digunakan kembali).

  • Bahkan jika menandatangani dokumen yang sama, hasil tanda tangan (r, s) juga harus berbeda.

  • Siapa pun dapat memverifikasi apakah jejak tersebut dibuat dengan stempel Anda (kunci publik memverifikasi tanda tangan).

  • Tetapi tidak ada yang dapat mengungkapkan stempel Anda (kunci privat) dari jejak tersebut, selama k aman.

Untuk memastikan keamanan k, ECDSA menggunakan spesifikasi RFC 6979, menggunakan generator angka acak deterministik (HMAC_DRBG atau HKDF) untuk menghitung k. Saat menghasilkan k, gunakan kunci privat d dan pesan m sebagai benih, memastikan k tetap tetap pada input yang sama. Proses perhitungan adalah sebagai berikut:

Poin kunci:

1. k harus acak dan unik, jika tidak, akan menyebabkan kebocoran kunci privat.

2. Jika k sama pada dua tanda tangan, penyerang dapat dengan mudah memulihkan kunci privat d langsung dari dua tanda tangan (r, s1) dan (r, s2).

Penurunan matematis:

Misalkan pengguna menandatangani dua pesan berbeda m1 dan m2, tetapi angka acak k secara tidak sengaja sama, maka hasil tanda tangan adalah sebagai berikut:

Penyerang dapat menghitung selisih antara s1 dan s2:

Karena m1, m2, s1, dan s2 semuanya diketahui, penyerang dapat menghitung k:

Kemudian, masukkan k ke dalam persamaan tanda tangan asli untuk mendapatkan kunci privat d:

Analisis kerentanan

Perpustakaan elliptic seharusnya menggunakan HMAC_DRBG (generator angka acak deterministik) untuk memastikan k yang unik setiap kali tanda tangan. Namun, karena kesalahan dalam pemrosesan input, dalam beberapa kasus pesan yang berbeda dapat menghasilkan k yang sama, yang mengakibatkan kebocoran kunci privat.

Kode generasi k

Dalam metode sign() dari perpustakaan elliptic, k dihasilkan oleh generator angka acak pseudo HMAC_DRBG, kode terkait adalah sebagai berikut:

elliptic/lib/elliptic/ec/index.js

Parameter input HMAC_DRBG terutama mencakup:

  • entropi (entropy) → di sini menggunakan kunci privat bkey, memastikan HMAC_DRBG memiliki cukup keacakan.

  • nonce (benih angka acak) → dihitung dari msg, memengaruhi nilai akhir k yang dihasilkan.

Karena HMAC_DRBG bersifat deterministik, entropi dan nonce yang sama akan menghasilkan k yang sama. Jika nonce sama dalam proses tanda tangan yang berbeda, maka k juga akan sama, yang akan menyebabkan kebocoran kunci privat.

Penyebab kerentanan: Konversi BN menyebabkan penggunaan kembali nonce

Dalam implementasi elliptic, msg diubah menjadi BN (Big Number), kemudian nonce dihitung dari msg.toArray():

Titik masalah:

  • msg adalah instansi BN, tetapi nonce adalah array.

  • Instansi BN yang berbeda mungkin menghasilkan array yang sama setelah konversi.

  • Ini berarti bahwa nonce yang sama dapat dihasilkan untuk pesan yang berbeda, yang akhirnya mengarah pada penggunaan kembali k.

Jika k digunakan kembali pada dua pesan berbeda msg1 dan msg2, maka kunci privat d dapat dipulihkan melalui rumus matematis yang disebutkan di atas.

Penyerang dapat membangun msg khusus sehingga noncenya sama, yang menyebabkan penggunaan kembali k. Hanya perlu mendapatkan sepasang (r, s1) dan (r, s2) untuk memulihkan kunci privat d melalui perhitungan matematis. Yang lebih serius, penyerang dapat membangun msg jahat semacam itu untuk pasangan pesan/tanda tangan yang diketahui, yang berarti selama penyerang dapat membujuk korban untuk menandatangani satu pesan jahat, penyerang dapat sepenuhnya memulihkan kunci privat dan memalsukan tanda tangan apa pun.

Eksploitasi kerentanan (PoC)

Perpustakaan elliptic memungkinkan penggunaan string heksadesimal sebagai salah satu jenis input, yang saat penandatanganan akan mengubah msg menjadi instansi BN, kemudian mengubahnya menjadi tipe array. Jika dua pesan berbeda menghasilkan array yang sama setelah konversi, nonce juga akan sama, yang akhirnya mengarah pada penggunaan kembali k.

Contoh kode:

Output yang dijalankan:

msg1 = "message" dan msg2 = "-message" setelah konversi BN, menghasilkan nonce yang sama (desain elliptic menerima string heksadesimal sebagai salah satu jenis input yang mungkin). Karena nonce sama, menyebabkan k yang dihasilkan oleh HMAC_DRBG juga sama. Dari hasil tanda tangan dapat dilihat bahwa nilai r sama persis, yang merupakan manifestasi langsung dari penggunaan kembali k.

Proses serangan lengkap:

1. Simulasikan korban menandatangani pesan yang normal, dapatkan pesan msg0 dan tanda tangan sig0.

2. Penyerang membangun pesan jahat msg1 dan membujuk korban untuk melakukan tanda tangan, mendapatkan sig1.

3. Memanfaatkan kerentanan penggunaan kembali k, menghitung k dari (r, s1) dan (r, s2).

4. Menggunakan k untuk lebih lanjut menghitung kunci privat d, serangan berhasil.

Contoh kode:

Output yang dijalankan:

Saran Perbaikan

1. Tingkatkan elliptic ke 6.6.1+, masalah ini telah diperbaiki secara resmi.

2. Hindari menandatangani pesan yang tidak terverifikasi secara langsung, pastikan msg telah distandarisasi.

3. Ganti kunci privat yang mungkin terpengaruh.

Kesimpulan

Kerentanan ini mengungkapkan sensitivitas ekstrem dari tanda tangan ECDSA terhadap angka acak k. Begitu k digunakan kembali, kunci privat dapat dipulihkan secara langsung, dan penyerang dapat sepenuhnya mengendalikan aset dan identitas pengguna. Pengembang harus segera memperbaiki kerentanan dan ketat dalam mengatur pemrosesan input untuk memastikan keamanan tanda tangan ECDSA. Akhirnya, terima kasih kepada dompet Rabby yang menyediakan informasi kerentanan.

Referensi

[1] https://github.com/indutny/elliptic/security/advisories/GHSA-vjh7-7g9h-fjfh
[2] https://paulmillr.com/posts/deterministic-signatures