CVE-2024-24813: Biến thể SQL Injection trong Frappe Framework

·

3 min read

CVE-2024-24813 được mình tìm ra trong một dịp khá thú vị. Nay bên vendor họ đã patch xong, mình viết bài blog này chia sẻ về cách mình tìm ra nó như thế nào.

Sáng hôm đó, mình ngồi review lại code để chuẩn bị cho buổi training phân tích 1-day với mấy anh em CyberJutsu. Sau khi phân tích & viết exploit xong, mình tính nói một chút idea về cách tìm biến thể của lỗi bảo mật. Vì mình nghĩ bỏ công sức phân tích 1-day đã đời, sao không đi thêm một bước nữa biết đâu tìm ra 0-day?

Tìm một hồi thì ra 0-day thật, nên phần tìm biến thể trong buổi training bị remove = ))

Technical Details

Bắt đầu từ bug 5 năm trước: CVE-2019-14966

Note: Chi tiết phân tích CVE-2019-14966 đã được bạn #NemNem phân tích rất kỹ ở đây. Trong phần này, mình đi thẳng vào đoạn tìm biến thể.

Khi tìm bug white-box nói chung, mình rất hay để ý "phong cách code" của developer. Trong trường hợp của CVE-2019-14966 là về lỗi SQL Injection, mình sẽ để ý xem developer thường hay code các câu SQL query thế nào.

Cùng xem đoạn code bên dưới ↓


# https://github.com/frappe/frappe/blob/v12.0.3/frappe/model/db_query.py#L118-L119
def build_and_run(self):
    # ... snippet code ...
    query = """select %(fields)s from %(tables)s %(conditions)s
        %(group_by)s %(order_by)s %(limit)s""" % args

    return frappe.db.sql(query, ...)

Đây là đoạn code xây dựng câu SQL query liên quan đến CVE-2019-14966. Các bạn có để ý "phong cách code" ở đây là gì không?

Developer sử dụng cú pháp format string, một cách xử lý chuỗi rất thô sơ, để cấu tạo nên câu SQL query. Thay vì sử dụng các hàm prepared statement chính thống để cho thư viện kiểm tra untrusted data, thì developer chọn cách tự mình validate và tự build câu SQL query. CVE-2019-14966 nguyên nhân cũng đến từ việc validate các biến fields, group_by, order_by không chuẩn dẫn tới SQL Injection.

"Code kiểu này thế nào cũng bị bug nữa!"

Trong team mình, lâu lâu bạn sẽ nghe câu này được thốt lên = ))

Mình tin lần này cũng vậy, code kiểu format string như vầy thế nào cũng bị bug nữa. Bây giờ quan trọng là làm sao tìm ra những dòng code kiểu kiểu giống như vầy (còn gọi là bug pattern) trong đống source code của Frappe Framework.

Có rất nhiều công cụ code scanning hiện đại giúp security engineer làm được điều này, có thể kể đến như: SemGrep, CodeQL, etc. Nhưng lúc đó mình dùng RegEx cho lẹ. Câu RegEx mình dùng ↓

"""select(.*\n)*.*"""\s*%

Đại ý câu RegEx này có nghĩa là:

  • Tìm chuỗi bắt đầu bằng 3 dấu nháy kép """ và chữ select (có thể đôi thành insert, update, ... sau)

  • Sau đó, muốn xuống hàng bao nhiêu lần cũng được

  • Phần cuối kết thúc bằng 3 dấu nháy kép """, khoảng cách \s, và ký tự % (cú pháp format string của Python)

  • Anh em có thể lên trang https://regex101.com/ test để hiểu kỹ hơn

Kết quả có 38 files match ↓

Sau một hồi review 38 kết quả, mình tìm ra 1 chỗ bị bug nằm trong file report_dump.py. Flow từ untrusted data chạy đến hàm thực thi câu SQL query được mình tóm tắt ở hình bên dưới.

Câu RegEx của mình khá thô sơ, bạn đọc có thể sửa lại và tìm 0-day của riêng mình.

Gud luck anh em <3

References

https://github.com/frappe/frappe/security/advisories/GHSA-fxfv-7gwx-54jh