Lưu ý: các phân tích trong bài viết này được dựa trên phiên bản Btalk 1.0.6 tải về từ PlayStore. Các vấn đề được nêu trong bài viết này BKAV đã được gửi email thông báo từ trước.

(pdah – cb_ – k9)

Cơ chế đăng ký và kích hoạt

Quá trình xác thực trên điện thoại của Btalk  gồm 3 bước chính:

  1. Đăng ký tài khoản với Btalk:
    • Người dùng gửi thông tin về số điện thoại đang sử dụng cho máy chủ Btalk.
    • Btalk gửi tin nhắn kèm mã số xác thực đến số điện thoại người dùng.
  2. Xác nhận (kích hoạt) tài khoản:
    • Mã số xác thực được nhập và gửi lại Btalk
    • Btalk xác nhận đúng mã xác thực và gửi lại mật khẩu khởi tạo riêng cho mỗi phiên đăng ký.
  3. Đăng ký với hệ thống nhắn tin OpenSIPS
    • Mật khẩu kèm thông tin số điện thoại được gửi lại hệ thống OpenSIPS của Btalk
    • Btalk xác nhận đã nhận được thông tin

Bước 1 – Đăng ký tài khoản với Btalk:

Sau khi người dùng nhập số điện thoại, một HTTP GET Request được gửi từ ứng dụng Btalk đến API https://bmail.vn/service/preauth với các tham số sau:

domain       = bmail.vn
from         = bphone
reqType      = bphoneRegister
txtDomain    = bmail.vn
useServerSms = true
txtUser      = <số điện thoại kèm mã quốc gia>@bmail.vn
timestamp    = <timestamp>
preauth      = <hash>

Tham số preauth được sử dụng để “ký” các thông tin được thiết bị gửi đến máy chủ Btalk. Mục đích chính là để xác nhận thông tin được gửi từ một nguồn hợp lệ. Điều này được thể hiện qua việc API gửi về isTrustedDomain=false nếu chúng ta gửi giá trị ngẫu nhiên cho preauth.

Giá trị của preauth được xác định như sau:

preauth = HMAC_SHA1(key, domain + ’|’ + timestamp + ‘|’ + txtUser)

Nếu thông tin được ký đúng, API trả về một HTTP Response rỗng đồng thời hệ thống gửi một tin nhắn chứa mã số xác thực (gồm 4 ký số) đến số di động của người dùng. Nội dung của tin nhắn có dạng:

Your Btalk code is XXXX.

Bước 2 – Xác nhận tài khoản

Sau khi có mã số xác thực, ứng dụng Btalk tiếp tục gửi một HTTP GET Request đến https://bmail.vn/service/preauth với danh sách các tham số:

txtDomain   = bmail.vn
from        = bphone
reqType     = bphoneRegister
op          = verifyCode
txtDomain   = bmail.vn
txtUser     = <số điện thoại kèm mã quốc gia>@bmail.vn
timestamp   = <timestamp>
verifyCode  = <code>
preauth     = <hash>

Với preauth được xác định bằng:

preauth = HMAC_SHA1(key, timestamp + ’|’ + txtUser + ‘|’ + verifyCode)

Nếu Btalk gửi đúng mã xác nhận, API sẽ gửi về mật khẩu (gồm 8 ký tự). Trên lý thuyết, với số điện thoại và mật khẩu này, chúng ta có thể gửi và nhận tin nhắn thông qua máy chủ của Btalk. Tuy nhiên chúng ta không thể làm điều đó nếu chưa thực hiện bước tiếp theo.

Bước 3 – Đăng ký với hệ thống nhắn tin OpenSIPS

Ứng dụng Btalk gửi một HTTP POST Request đến dịch vụ OpenSIPS tại https://btalk.bkav.com:8443/OpenSipsServices/rest/sip/register

Với tham số:

username  = <số điện thoại kèm mã quốc gia>
password  = <mật khẩu>
timestamp = <timestamp>
preauth   = <hash>

Trong đó

preauth = HMAC_SHA1(key, timestamp + ’|’ + password + ‘|’ + username + ‘@bmail.vn’)

Sau bước này ứng dụng Btalk đã có thể đăng nhập vào hệ thống XMPP của Btalk đồng thời gửi, nhận tin nhắn.

Các vấn đề

Lộ key (khóa) của hàm băm mã hóa

Như chúng tôi mô tả ở trên, Btalk sử dụng HMAC-SHA1 để ký các HTTP requests gửi đi từ ứng dụng trên điện thoại. Về cơ bản HMAC-SHA1 giúp người sử dụng kiểm tra tính đồng nhất của dữ liệu, đồng thời chứng thực phía gửi dữ liệu thông qua tính bí mật của khóa key. Tuy nhiên giá trị của key được nhúng trong ứng dụng Btalk và chúng ta có thể tìm thấy nó dễ dàng:

Configuration.REGISTER_KEY = "41ab3e484f918ff0d378058e50eb0f79e93d19383ca1053830a878a83bcce3fc";

Điều này dẫn đến việc bất kỳ ai cũng có thể giả mạo ứng dụng Btalk để gửi HTTP requests đến máy chủ.

Spam tin nhắn xác thực đến số di động khác

Một hệ quả của vấn đề trên là chúng ta có thể spam số máy di động khác bằng cách liên tục giả lập bước một của cơ chế xác thực.

Lấy cắp thông tin đăng nhập thông qua vét cạn mã xác thực

Ở bước một và hai, mã xác thực chỉ có 4 ký số và không bị vô hiệu trong khoản thời gian nhất định, chúng ta có thể liên tục lặp lại bước hai với tối đa 10.000 khả năng khác nhau của mã xác thực cho đến khi nào nhận được mật khẩu.

Thời gian ước tính để thử chọn hết tất cả các khả năng (10.000 trường hợp) là khoảng 1 giờ đồng hồ. Giả sử nhà phát triển lựa chọn phương án vô hiệu hóa mã xác thực trong 5 phút, chúng ta sẽ có cơ hội thử chọn khoảng 830 trường hợp, đồng nghĩa với khoảng 8.3% cơ hội thành công. Đây vẫn là một con số không nhỏ.

SSL vẫn không an toàn

Mặc dù Btalk sử dụng SSL cho tất cả các bước xác thực người dùng, nhưng nhà phát triển cho phép ứng dụng chấp nhận tất cả các certificate mà nó nhận được bằng cách:

  • Định nghĩa lại lớp BkavSSLSocketFactory thay cho lớp SSLSocketFactory mặc định và bỏ hết tất cả các bước chứng thực certificate.
  • Bỏ qua việc kiểm tra hostname của certificate: KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null, null); BkavSSLSocketFactory socketFactory = new BkavSSLSocketFactory(keyStore); (()SSLSocketFactory)socketFactory).setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

Điều này cho phép hacker có thể thực hiện tấn công MITM và “nhìn lén” tất cả dữ liệu của người dùng.

Một vài điều thú vị khác

Giả lập gửi và nhận tin nhắn từ sau khi có được mật khẩu

1. Trên điện thoại (đã root) hoặc emulator:

Sau khi cài Btalk, điều chỉnh thông tin đăng nhập của ứng dụng tại /data/data/bkav.android.btalk/shared_prefs/bkav.android.btalk_preferences.xml với số điện thoại và mật khẩu lấy được.

...

<string name="bkav_pref_sipx_password">YYYYYYYY</string>
<string name="bkav_pref_sipx_username">84XXXXXXXXXXX</string>

...

2. Thông qua viết mã

Đoạn mã đơn giản sau giả lập việc gửi và nhận tin nhắn qua Btalk:

import xmpp
import time

username = '84xxxxxxxx'
passwd   = 'yyyyyyy'
to       = '[email protected]'
msg      = 'Hello from a script'

def message_callback(client, stanza):
    sender       = stanza.getFrom()
    message_type = stanza.getType()
    message      = stanza.getBody()

print "Received '%s' from '%s'" % (message, sender)

client = xmpp.Client('bmail.vn', debug=[])
client.connect(server=('chat.bkav.com',5222))
client.auth(username, passwd, 'bmail.vn')
client.RegisterHandler("message", message_callback)

client.sendInitPresence()
message = xmpp.Message(to, msg)
message.setAttr('type', 'chat')
print "Sending message ..."
client.send(message)

print "Waiting for message ..."
while client.isConnected():
    client.send(xmpp.Presence())
    client.Process()
    time.sleep(1)

Cái gì đây ?

Chúng tôi phát hiện một vài đoạn mã thú vị trong ứng dụng Btalk:

Cho dù BKAV đã thay đổi tên đăng nhập và/hoặc mật khẩu của dịch vụ liên quan ở phía máy chủ, việc để lọt những dòng mã như thế này lên Play Store cho thấy nhà phát triển chưa làm tốt khâu kiểm định mã nguồn trước khi xuất xưởng.

(còn tiếp)