Aneka bug bypass OTP (One Time Password)

Berbagai website memiliki fitur two factor authentication (2FA) dalam bentuk OTP (One Time Password) alias password sekali pakai yang biasanya dikirim via SMS atau email. Sering kali ini tidak diimplementasikan dengan sempurna dan bisa dibypass. Tulisan ini tidak membahas metode baru, hanya sekedar koleksi beberapa OTP bypass yang sering saya temukan. Tujuan tulisan ini:

  • Untuk pentester supaya jadi checklist ketika mengecek OTP
  • Untuk programmer supaya jadi checklist ketika mengimplementasikan OTP

Tulisan ini juga untuk memberi pencerahan, supaya user sadar bahwa meskipun sebuah website memiliki OTP tidak berarti 100% aman. OTP biasanya diminta ketika login kali pertama, dan kadang hanya diminta ketika akan melakukan transaksi (contoh: ketika akan transfer uang). Kadang bypass OTP bisa dilakukan di login saja, transaksi saja, atau keduanya.

Sebagai catatan: OTP hanyalah salah satu bentuk dari 2FA, contoh lainnya adalah penggunaan hardware key. Implementasi OTP yang baik seharusnya tidak bisa dibypass. Jadi jika sebuah website tidak bisa dibypass OTP-nya dengan cara di sini, maka ada beberapa kemungkinan

  • Implementasi OTP website/aplikasi tersebut sudah aman dan memang tidak bisa dibypass
  • Anda kurang kreatif memikirkan bug lain yang mungkin spesifik terhadap aplikasi tersebut

OTP kosong

Kadang sebuah website membuat OTP jadi opsional, user bisa mengaktifkan OTP jika ingin lebih aman. Beberapa website mengecek ini dari ada atau tidaknya OTP yang dikirimkan dan dengan meremove field otp kadang OTP bisa dibypass.

OTP Non Numerik/Non String

Ini bypass sangat sederhana. Program backend berharap OTP adalah string, tapi bagaimana jika kita beri input berupa array atau object? Contohnya di PHP, parameter otp=123456 bisa dicoba diganti dengan otp[]=12356. Contoh lain jika backend menerima JSON: {"userid": "x123", "otp":"123456"} lalu kita ganti menjadi {"userid": "x123", "otp":[]} .

Salah satu penyebab ini bisa terjadi adalah penggunaan strcmp di PHP untuk membandingkan string dan array. Ini adalah salah satu kelemahan type juggling di PHP. Sementara di NodeJS atau teknologi javascript lain, bisa juga memiliki bug prototype pollution.

Intinya adalah: input-input non numerik/non string atau bahkan string dengan format tertentu bisa saja membuat error pada pemrosesan di backend.

SQL Injection/Akses Database

Bug SQL injection memungkinkan kita mengintip nilai OTP saat ini. Bahkan dalam kasus tertentu field OTP itu sendiri yang memiliki SQL injection. Hal ini bisa parah jika OTP yang dipakai adalah berbasis TOTP (time based OTP), dan kita memiliki seed untuk OTP tersebut. Artinya kita bisa mendapatkan OTP untuk sembarang orang kapanpun juga.

Tentunya akses database tidak harus selalu melalui SQL Injection, bisa saja melalui noSQL injection atau melalui Remote Code Execution (RCE). Jika kita bisa mendapatkan RCE kita bisa mengupload shell yang bisa mengakses database.

Bruteforce OTP

Jika tidak dibatasi waktu dan jumlah percobaan, maka OTP bisa dibypass dengan bruteforce terutama jika hanya 4 digit. Hanya dibutuhkan maksimum 10 ribu percobaan. Dalam kasus tertentu jumlah request bisa sangat sedikit. Kasus menarik yang pernah saya temui: OTP dibutuhkan untuk menambahkan email, dan format requestnya seperti ini:

actions: [{"action": "add_email",  "email": "[email protected]", "otp":"1232"}]

Menariknya di sini adalah adanya array actions, artinya kita bisa mengirimkan banyak request sekaligus, seperti ini:

actions: [
{"action": "add_email",  "email": "[email protected]", "otp":"0001"},  
{"action": "add_email",  "email": "[email protected]", "otp":"0002"},
{"action": "add_email",  "email": "[email protected]", "otp":"0003"} 
] 

Saya beruntung karena aplikasi tersebut meneruskan memproses action berikutnya jika yang pertama gagal. Karena hanya ada 10 ribu kemungkinan, kita cukup mengirimkan 100 request dengan masing-masing request mencoba 100 OTP. Atau bahkan jika sistemnya mampu menerima, kita mungkin bisa mengirimkan 1000 OTP, dan hanya butuh 10 request.

OTP dicek di client

Ini adalah bug yang sangat aneh dan seharusnya tidak terjadi. Ketika aplikasi butuh OTP, server mengembalikan nilai OTP ke client. Client di sini artinya browser atau mobile app. Jadi jika kita bisa mengintercept hasil requestnya, kita tahu apa OTPnya. Bug ini mungkin terdengar konyol, tapi setidaknya sudah ada 3 aplikasi yang saya cek yang memiliki bug sejenis ini.

Memakai OTP user lain

Ada OTP yang diimplementasikan dengan aneh: OTP tidak diasosiasikan dengan user id, tapi dengan OTP token. Atau sederhananya: OTP sesorang bisa dipakai orang lain.

Dalam bug ini, ketika user melakukan aksi yang butuh OTP, aplikasi meminta ke server (generateOTP) dan server akan mengembalikan sebuah token OTP (bukan string OTPnya, hanya sekedar ID misalnya xyz123) ke aplikasi sambil mengirimkan nilai OTP ke SMS User (misalnya 7171). Ketika user mengetikkan OTP dari SMS yang diterimanya, maka yang dikirimkan ke server adalah: aksi user saat ini (misalnya transfer uang), token OTP (xyz123) dan OTP-nya (7171).

Masalahnya token OTP ini bisa dipakai di user lain, jadi dengan mengirimkan transaksi sebagai user B, tapi memakai nilai OTP dari user A (dengan token OTP dan nilai OTP user A) kita bisa membypass OTP user B. Jadi dalam kasus ini: attacker A memiliki account juga di website target agar bisa menerima OTP di ponselnya.

OTP spamming

Terkadang SMS tidak langsung sampai, jadi user meminta OTP sampai beberapa kali. Kadang jika kita meminta OTP 3 kali, yang sampai ke HP malah hanya 2 OTP pertama (sementara OTP terakhir masih “nyangkut”). Karena ingin berbaik hati, sebagian programmer menerima tidak hanya OTP terakhir, tapi beberapa OTP sebelumnya. Kadang ini tidak berdasarkan jumlah (n OTP sebelumnya), tapi berdasarkan waktu, misalnya OTP yang dihasilkan 10 menit terakhir akan dianggap valid.

Kita bisa menspam server dengan meminta OTP sebanyak-banyaknya. Misalnya OTP hanya 4 digit dan selama 10 menit kita bisa meminta 1000 OTP, maka kemungkinan OTP kita benar adalah 1 banding 10.

Bug IDOR update profile/phone number

Biasanya kita bisa mengubah nomor telepon via aplikasi atau via web. Jika ada bug indirect object references (IDOR) sehingga kita bisa mengupdate/menimpa profil atau ponsel orang lain ketika kita melakukan aksi profile (yang seharusnya mengupdate profile kita sendiri), maka ini bisa digunakan untuk bypass OTP karena OTP akan dikirimkan ke nomor ponsel kita.

Sebagai catatan: kebanyakan bank (termasuk yang saya pakai) hanya mengijinkan perubahan nomor ponsel dengan datang langsung ke banknya. Sementara perubahan email berbeda kebijakannya (ada bank yang mengijinkan online, ada yang tidak, ada yang butuh OTP dan ada yang tidak).

Weak Random Number Generator

Ini hanya bisa ditemukan jika kita bisa mendapatkan akses ke source code melalui bug lain (atau mungkin adminnya lupa sehingga direktori .git bisa diakses). Kadang OTP dihasilkan hanya dari random number generator yang memakai waktu saat ini sebagai seed.

Logic Reset Password

Dalam banyak aplikasi, setelah kita reset password, maka kita otomatis login. Jika ada kelemahan dalam mekanisme reset password (ini mungkin akan saya bahas di kesempatan lain), maka kita bisa memanfaatkan ini untuk bypass OTP.

Cara lain (bukan bug aplikasi)

Sebagai pelengkap, saya akan menuliskan beberapa bypass OTP yang bukan bug aplikasi dan biasanya di luar scope sebuah bounty ataupun pentest.

Kebanyakan orang jahat memakai social engineering agar korban membacakan OTP. Ini menurut saya bukan bug aplikasi, terutama jika ada instruksi yang jelas di SMS: jangan berikan OTP ini pada siapapun. Jika instruksinya tidak ada atau tidak jelas, maka itu bisa disarankan kepada developernya.

Simjacking atau SIM swap adalah serangan social engineering ke perusahaan telekomunikasi. Intinya penyerang mengumpulkan data orang lain, dan menghubungi perusahaan telekomunikasi: “Maaf SIM card saya hilang, tolong buatkan SIM card baru dengan nomor yang sama, berikut ini data pribadi saya”. Dengan data yang cukup lengkap, penipu bisa meyakinkan agar diberikan SIM Card baru.

Cara lain bypass OTP adalah dengan mengakses sistem telekomunikasi, baik itu bug di level SS7 maupun di sistem lain yang berhubungan dengan SMS. Sebagai catatan: serangan ini cukup kompleks dan rawan ketahuan. Jadi biasanya hanya dilakukan jika targetnya nilainya cukup besar.

Cara berikutnya adalah dengan malware: jika ada malware yang terinstall di HP korban, maka semua SMS bisa diakses oleh attacker. Dengan cara ini OTP aplikasi apapun yang berbasis SMS bisa diakses oleh attacker. Agar korban menginstall aplikasi malware ini, korban bisa ditipu dengan social engineering tertarget (“bapak perlu menginstall aplikasi kami terbaru agar dapat mengakses layanan ini”) atau sekedar membuat aplikasi palsu dan disebar ke web ( misalnya: “APK terbaru cheat Mobile Legends”).

Jika OTP dikirim via email, dan email sudah diambil alih oleh attacker, maka attacker akan bisa mengakses OTP. Berbagai serangan untuk mengakses email orang bisa dilakukan, dan mungkin akan saya bahas juga di tulisan lain.

Penutup

Mungkin masih ada bug-bug lain yang tidak tercantum di sini, jadi jangan cuma terpaku pada list yang tercantum di sini. Intinya pikirkan dari mulai OTP dibuat, dikirimkan, sampai divalidasi di sisi server.

Ketika saya mendapatkan source code sebuah aplikasi, kadang saya terpikir beberapa bug yang tidak terpikirkan sebelumnya. Misalnya ternyata programmernya memiliki backdoor OTP, yaitu OTP sakti berupa string khusus yang bisa dipakai di sembarang transaksi (mungkin digunakan ketika testing tapi tidak dihapus dalam production).

Artikel singkat ini sekedar menjadi checklist ketika pentest untuk mengetahui serangan apa saja yang bisa dilakukan untuk bypass OTP. Saya juga berharap programmer membaca ini agar membuat sistem yang aman. Saya suka sistem yang aman karena membuat pekerjaan pentest saya jadi lebih cepat, dan saya jadi bisa memikirkan bug lain yang lebih kreatif.

Satu tanggapan pada “Aneka bug bypass OTP (One Time Password)”

Tinggalkan Balasan

Situs ini menggunakan Akismet untuk mengurangi spam. Pelajari bagaimana data komentar Anda diproses.