+14

Khai thác lỗ hổng Firebase Realtime DB trên Android

image.png

Khi kiểm thử xâm nhập ứng dụng di động, chúng ta thường xuyên phải kiểm tra các yếu tố bảo mật của dịch vụ Firebase. Vậy Firebase là gì? Tại sao các ứng dụng di động lại hay dùng Firebase? Những yếu tố về an toàn thông tin cần chú ý khi kiểm thử bảo mật cho ứng dụng di động là gì? Hãy cùng nhau thảo luận qua bài viết này nhé.

1. Dịch vụ Firebase

Firebase là một nền tảng phát triển ứng dụng giúp người dùng có thể dễ dàng xây dựng ứng dụng và nâng cấp dịch vụ. Firebase hiện tại thuộc quyền sở hữu của Google, và là một trong số các dịch vụ Cloud do Google cung cấp. Firebase bao gồm nhiều dịch vụ như:

  • Realtime Database
  • Firestore
  • Authentication
  • Hosting
  • A/B testing
  • Analytic

ảnh.png

Nhờ có số lượng dịch vụ nhiều, cài đặt dễ dàng, bảo mật cao, và hỗ trợ tốt cho nền tảng di động nên Firebase thường được các ứng dụng Android (và cả iOS) sử dụng để lưu trữ dữ liệu, quản lý tài khoản, lưu thông tin các lần ứng dụng gặp lỗi.

2. Thông tin cấu hình Firebase

Khi cấu hình Firebase, chúng ta sẽ được trang web hướng dẫn đầy đủ từng bước. Trong đó có một bước cần copy tệp tin google-service.json vào trong thư mục của dự án. Trong tệp tin này chứa các thông tin giúp ứng dụng có thể kết nối tới các API Firebase tương ứng của ứng dụng.

Trong đó có một số thông tin quan trọng như sau:

  • Firebase URL: liên kết dẫn tới Realtime DB
  • Storage bucket: liên kết dẫn tới Cloud storage
  • Auth domain: địa chỉ thực hiện xác thực tài khoản. Thông tin này không được đề cập rõ, tuy nhiên nó là [project_id].firebaseapp.com
  • API key: để sử dụng các API của Firebase

Bốn thông tin trên có thể giúp ứng dụng truy cập được vào Firebase, thì cũng có thể giúp hacker truy cập được vào Firebase của dự án. Tuy nhiên, chúng lại không được bảo vệ thật tốt.

Sau khi biên dịch ra tệp tin APK rồi thì sẽ không thấy tệp tin google-service.json đâu nữa. Khi decompile tệp tin bằng apktool hay giải nén tệp tin APK đều không thấy dấu hiệu của tệp tin này. Đó là do trình biên dịch đã đọc những thông tin có trong tệp tin google-service.json rồi chuyển đến nơi khác tiện cho ứng dụng sử dụng hơn.

Và bốn thông tin cần để truy cập vào Firebase đều được đặt chung một nơi. Đó là trong tệp tin strings.xml

3. Khai thác lỗ hổng Firebase Realtime DB

Dịch vụ Firebase Realtime DB cho phép người dùng tuỳ ý cấu hình bộ luật truy cập dữ liệu.

Các lỗ hổng của dịch vụ Firebase đa số là do cấu hình không đủ an toàn, cấu hình sai. Việc khai thác là lợi dụng việc cấu hình yếu này để đọc được dữ liệu trong Firebase, thậm chí là có thể thay đổi dữ liệu.

3.1. Lỗi cấu hình đọc/ghi tuỳ ý

3.1.1. Khai thác lỗi cấu hình quyền đọc

Lỗ hổng này tồn tại do cấu hình quyền đọc/ghi là true. Nguy hiểm nhất là cài đặt phạm vi trên toàn bộ cơ sở dữ liệu.

Với cấu hình như trên, bất cứ ai cũng có thể đọc toàn bộ dữ liệu, nhưng không thể ghi bất cứ điều gì vào đây. Để khai thác trong trường hợp này, chúng ta sẽ thực hiện như sau:

  • Decompile tệp tin apk bằng công cụ apktool: java -jar apktool.jar d [tệp-tin-apk]
  • Mở tệp tin /res/values/strings.xml và tìm với từ khoá firebasedatabase.
  • Mở trình duyệt, thêm /.json vào cuối đường dẫn Realtime DB và truy cập.

Vì đã mở toàn quyền đọc nên toàn bộ dữ liệu trong Realtime DB sẽ được hiển thị dưới dạng cấu trúc JSON.

Trong trường hợp không có quyển đọc sẽ chỉ có thông báo Permission denied.

3.1.2. Khai thác lỗi cấu hình quyền ghi

Ở trường hợp này, quyền ghi đã được mở. Do đó chúng ta có thể thử ghi thêm một chút dữ liệu vào Realtime DB.

Để kiểm tra, chúng ta sẽ gửi một POST/PUT request đến đường dẫn của Realtime DB. Nếu response code là 200 thì tức là quyền ghi đã được mở. Đồng thời dữ liệu mới đã được chèn thêm vào Realtime DB. VD một đoạn code Python dưới đây sử dụng thư viện request để thực hiện HTTP request.

import requests

url = "Firebase Realtime DB URL"
new_file = url + "/" + "pentest.json"
new_data = {'Exploit': 'successfull', 'Data': '1337'}

response = requests.put(new_file, json=new_data)

if (response.status_code == 200):
    print("True")
else:
    print("False")

Kết quả:

3.1.3. Cấu hình quyền đọc/ghi trên một số document

Đôi khi quyền đọc/ghi vẫn chưa được cấu hình tốt, nhưng việc cho phép đọc/ghi trên toàn bộ cơ sở dữ liệu đã bị hạn chế. Có thể quyền đọc/ghi chỉ được cho phép trên 1 số document mà thôi, VD như:

Trong trường hợp như trên, khi truy cập đường dẫn [Firebase Realtime DB URL]/.json sẽ bị thông báo Permission denied ngay. Nhưng nếu truy cập đường dẫn [Firebase Realtime DB URL]/users.json thì có thể đọc được toàn bộ dữ liệu trong document users. Tương tự với quyền ghi.

Trong trường hợp này, chúng ta cần kiên nhẫn thử toàn bộ các document. Có thể nhờ dịch ngược ứng dụng để tìm được các document, đoán dựa trên chức năng của ứng dụng, hoặc fuzzing.

3.2. Lỗi cấu hình đọc/ghi cho tài khoản đã đăng nhập

Trong trường hợp này, người cấu hình Firebase Realtime DB đã cẩn thận hơn. Họ đã sử dụng các cú pháp do Firebase cung cấp để cấu hình quyền tốt hơn như:

  • ".read": "auth.uid !== null" cho phép đọc nếu đã xác thực thành công bằng Firebase Authentication.
  • ".write": "!data.exists()" cho phép ghi nếu dữ liệu chưa tồn tại.
  • ".read": "auth.uid === $uid" chỉ cho phép đọc dữ liệu thuộc về tài khoản đó.
  • .....

Trong trường hợp chỉ giới hạn quyền đọc/ghi chỉ cho tài khoản đã đăng nhập thì cần thực hiện theo các bước sau:

  • Bước 1: tìm trong strings.xml 4 thông tin sau
    • firebase_database_url
    • google_api_key
    • google_storage_bucket
    • project_id
  • Bước 2: tạo ứng dụng với Python, sử dụng thư viện pyrebase
    import pyrebase
    
    config = {
      "apiKey": "[google_api_key]",
      "authDomain": "[project_id].firebaseapp.com",
      "databaseURL": "[firebase_database_url]",
      "storageBucket": "[google_storage_bucket]"
    }
    
    firebase = pyrebase.initialize_app(config)
    auth = firebase.auth()
    
    email = "user@gmail.com"
    password = "123123"
    
    user = auth.sign_in_with_email_and_password(email, password)
    
    db = firebase.database()
    print(db.child("Users").get().val())    # Đối với document không cần đăng nhập
    print(db.child("Posts").get(user.get("idToken")).val())   # Đối với document cần đăng nhập
    

Đoạn mã Python trên tương ứng với việc tạo một ứng dụng bằng Python có kết nối với dịch vụ Firebase. Nhờ thế mà có thể đăng ký tài khoản, đăng nhập, truy vấn các dữ liệu,... như khi sử dụng ứng dụng Android.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí