+12

Phân tích CVE-2023-23488 Paid Memberships Pro < 2.9.8 - Unauthenticated Blind SQLi và vấn đề với request trong Wordpress

Xin chào mọi người, đã lâu rồi mình mới viết một bài phân tích CVE. Dạo gần đây xoay quanh nhiều chuyện 🍚👕🌾💸 nên ít thời gian để viết lách. Bài viết này là một cơ hội tình cờ để mình tìm kiếm lỗ hổng trên WordPress. Tuy nhiên, mình đã bỏ qua rất nhiều trường hợp, và mình muốn chia sẻ với các bạn về một lỗ hổng cụ thể.

CVE-2023-23488

CVE-2023-23488 được miêu tả rõ ràng tại đường link https://www.tenable.com/security/research/tra-2023-2. Đây là một lỗ hổng Unauth SQL injection ảnh hưởng đến plugin WordPress Paid Memberships Pro phiên bản < 2.9.8. Kẻ tấn công có thể gửi các truy vấn SQL tùy ý đến cơ sở dữ liệu của trang web thông qua tham số code của đường dẫn REST /pmpro/v1/order.

Để khai thác lỗ hổng này, có thể sử dụng payload TimeBased SQL Injection thông thường. Cách khai thác rất đơn giản như sau (không cần xác thực):

GET /?rest_route=/pmpro/v1/order&code=a%27%20OR%20(SELECT%201%20FROM%20(SELECT(SLEEP(1)))a)--%20- HTTP/1.1

Vậy tại sao lại dẫn tới lỗ hổng này, cài đặt và phân tích code plugin này theo version 2.9.7 có chứa lỗ hổng tại: https://downloads.wordpress.org/plugin/paid-memberships-pro.2.9.7.zip

Cực kỳ dễ dàng chúng ta có thể tìm thấy lỗ hổng được thấy ở đây. Sử dụng REST API của Wordpress được đăng ký theo router

image.png

nhảy vào function pmpro_rest_api_permissions_get_order() với

GET /?rest_route=/pmpro/v1/order HTTP/1.1

Sau đó truyền thêm tham số code vào khởi tạo MemberOrder, nhảy trực tiếp đến __construct

image.png

Ở đây ta truyền giá trị code không phải là số sẽ nhảy sang function getMemberOrderByCode($id)

image.png

Tại đây giá trị code được truyền vào hoàn toàn không được filter hay escape gì, tại đây chúng ta có thể inject SQL vào đây theo kiểu TimeBased như PoC bên trên mình đã nêu. Mọi bước thực hiện đều không cần xác thực.

image.png

Nhưng điều kỳ lạ ở đây là mình hoàn toàn, thường xuyên gặp những đoạn code viết kiểu này trên Wordpress, mình thường xuyên bỏ qua những đoạn code này vì nó biến được ngăn cách bởi '" . $code . "'khi này Wordpress sẽ tự động thêm slash vào mỗi khi truyền ' hoặc " thành \' hoặc \" => không tấn công SQL Injection được do không thể bypass qua dấu ' hoặc "

Addslash tại Wordpress

Test thử với đoạn code sau

$code = $_GET['code'];
var_dump("SELECT id FROM $wpdb->pmpro_membership_orders WHERE code = '" . $code . "' LIMIT 1");
die();

Với response từ Wordpress

image.png

Chúng ta có thể thấy rằng giá trị $_GET['code'] đã tự động được thêm slash vào, từ đây chúng ta không thể nào break ra khỏi nháy đơn để thoát khỏi string. Từ đây mình luôn tưởng viết code theo kiểu bên trên là an toàn do mình không thể thoát khỏi nháy đơn!!

Sau một hồi hỏi ChatGPT, WordPress sẽ tự động xử lý và áp dụng addslashes() cho các giá trị này để đảm bảo rằng chúng an toàn khi được sử dụng trong các câu lệnh SQL hoặc các tình huống khác. Hàm được gọi lên ngay từ wp-setting.php

image.png

Với ghi chú rõ ràng rằng sử dụng để escape cho wpdb

image.png

image.png

Mình đã test thử thì với mọi $_GET, $_POST, $_COOKIE, $_SERVER khi truyền từ input người dùng thì đều sẽ bị thêm slash vào.

Tuy nhiên, tại sao khi sử dụng REST API thì Wordpress lại xoá mất dấu slash đi, điều này dẫn đến lỗ hổng CVE-2023-23488. Chúng ta có thể break ra khỏi dấu nháy đơn, inject câu lệnh SQL tuỳ ý.

image.png

Vậy tại sao lại như thế, Wordpress thêm slash xong rồi lại xoá slash đi là sao?? Lại đi hỏi ChatGPT thì hoá ra WordPress có sử dụng một cơ chế để loại bỏ dấu backslash \ được thêm vào khi sử dụng REST API.

Tại đây request được gửi tới REST API sẽ được load vào rest_api_loaded(), sau đó tiếp tục được handle tại WP_REST_Server::serve_request()

image.png

Và được unslash() tại đây

image.png

Thật vậy, thử var_dump($request) chúng ta được kết quả

image.png

Kết luận

Vậy mình đã bỏ qua kha khá nhiều lỗ hổng SQL Injection vì bỏ qua cách viết nối chuỗi này. Lúc nào mình cũng tin rằng không thể bypass được qua dấu nháy khi code dạng đó 😦. Với REST API của Wordpress thì hoàn toàn có thể.

Một số cách phòng tránh lỗ hổng SQL Injection khi sử dụng REST API trong WordPress, ví dụ:

  • Sử dụng esc_sql() filter input từ người dùng truyền vào
  • Sử dụng prepare() để truy vấn an toàn trong Wordpress
  • ...

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í